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 package ffx.algorithms.thermodynamics;
39
40 import ffx.algorithms.AlgorithmListener;
41 import ffx.algorithms.cli.DynamicsOptions;
42 import ffx.algorithms.dynamics.MDVerbosity;
43 import ffx.algorithms.dynamics.MDWriteAction;
44 import ffx.algorithms.dynamics.MolecularDynamics;
45 import ffx.algorithms.dynamics.integrators.IntegratorEnum;
46 import ffx.algorithms.dynamics.thermostats.ThermostatEnum;
47 import ffx.algorithms.mc.BoltzmannMC;
48 import ffx.algorithms.mc.LambdaMove;
49 import ffx.algorithms.mc.MDMove;
50 import ffx.algorithms.thermodynamics.OrthogonalSpaceTempering.Histogram;
51 import ffx.numerics.Potential;
52 import ffx.potential.MolecularAssembly;
53 import ffx.potential.utils.EnergyException;
54 import org.apache.commons.configuration2.CompositeConfiguration;
55
56 import java.io.File;
57 import java.util.EnumSet;
58 import java.util.logging.Level;
59 import java.util.logging.Logger;
60
61 import static ffx.utilities.Constants.NS2SEC;
62 import static java.lang.String.format;
63 import static java.lang.System.arraycopy;
64 import static java.lang.System.nanoTime;
65 import static org.apache.commons.math3.util.FastMath.abs;
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 public class MonteCarloOST extends BoltzmannMC {
93
94
95
96
97 private static final Logger logger = Logger.getLogger(MonteCarloOST.class.getName());
98
99
100
101
102 private final Level mcLogLevel;
103
104
105
106 private final MDVerbosity mdVerbosity;
107
108
109
110
111 private static final double ENERGY_CONSERVATION_TOLERANCE = 10.0;
112
113
114
115 private final Potential potential;
116
117
118
119 private final OrthogonalSpaceTempering orthogonalSpaceTempering;
120
121
122
123 private final MDMove mdMove;
124
125
126
127 private final int biasDepositionFrequency;
128
129
130
131 private long totalSteps;
132
133
134
135 private final long stepsPerMove;
136
137
138
139 private final LambdaMove lambdaMove;
140
141
142
143 private double lambda = 1.0;
144
145
146
147 private boolean equilibration = false;
148
149
150
151 private boolean automaticWriteouts = true;
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166 public MonteCarloOST(Potential potentialEnergy, OrthogonalSpaceTempering orthogonalSpaceTempering,
167 MolecularAssembly molecularAssembly, CompositeConfiguration properties,
168 AlgorithmListener listener, DynamicsOptions dynamics,
169 boolean verbose, int cycleLength, File dynRestartFile) {
170 this.potential = potentialEnergy;
171 this.orthogonalSpaceTempering = orthogonalSpaceTempering;
172 mcLogLevel = verbose ? Level.INFO : Level.FINE;
173 mdVerbosity = verbose ? MDVerbosity.QUIET : MDVerbosity.SILENT;
174 stepsPerMove = cycleLength;
175 totalSteps = dynamics.getNumSteps();
176
177 ThermostatEnum thermostat = dynamics.thermostat;
178 if (!thermostat.equals(ThermostatEnum.ADIABATIC)) {
179 logger.info(format(" MC-OST MD moves will use the Adiabatic thermostat (%s ignored).", thermostat));
180 dynamics.setThermostat(ThermostatEnum.ADIABATIC);
181 }
182
183 IntegratorEnum integrator = dynamics.integrator;
184 if (!integrator.reversible || !integrator.deterministic) {
185 logger.info(format(" MC-OST MD moves require a reversible, deterministic integrator (Verlet replaced %s).", integrator));
186 dynamics.setIntegrator(IntegratorEnum.VERLET);
187 }
188
189
190 boolean useOST = properties.getBoolean("mdmove-full", false);
191 Potential mdPotential = useOST ? orthogonalSpaceTempering : potential;
192 mdMove = new MDMove(molecularAssembly, mdPotential, listener, dynamics, stepsPerMove, dynRestartFile);
193 if (properties.containsKey("randomseed")) {
194 int randomSeed = properties.getInt("randomseed", 0);
195 logger.info(format(" Setting random seed for lambdaMove to %d ", randomSeed));
196 lambdaMove = new LambdaMove(randomSeed, orthogonalSpaceTempering);
197 setRandomSeed(randomSeed);
198 } else {
199 lambdaMove = new LambdaMove(orthogonalSpaceTempering);
200 }
201
202
203 boolean discreteLambda = orthogonalSpaceTempering.getHistogram().hd.discreteLambda;
204 if (discreteLambda) {
205 lambdaMove.setContinuous(false);
206
207 lambdaMove.setMoveSize(orthogonalSpaceTempering.getHistogram().hd.lambdaBinWidth);
208 }
209
210
211 orthogonalSpaceTempering.setPropagateLambda(false);
212 int frequency = properties.getInt("mc-ost-bias-frequency", 1);
213 if (frequency <= 1) {
214 biasDepositionFrequency = 1;
215 } else {
216 biasDepositionFrequency = frequency;
217 logger.info(format(" MC-OST will deposit a bias only once per %d MC cycles.", biasDepositionFrequency));
218 }
219 }
220
221
222
223
224
225
226 public double getLambda() {
227 return lambda;
228 }
229
230
231
232
233
234
235 public void setLambda(double lambda) {
236 this.lambda = lambda;
237 orthogonalSpaceTempering.setLambda(lambda);
238 }
239
240 public MolecularDynamics getMD() {
241 return mdMove.getMD();
242 }
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257 public void sampleOneStep() {
258
259 lambda = orthogonalSpaceTempering.getLambda();
260 lambda = lambdaMove.validateLambda(lambda);
261 orthogonalSpaceTempering.setLambda(lambda);
262
263 int n = potential.getNumberOfVariables();
264 double[] gradient = new double[n];
265 double[] currentCoordinates = new double[n];
266 double[] proposedCoordinates = new double[n];
267 long numMoves = totalSteps / stepsPerMove;
268 int acceptMCOST = 0;
269
270
271 potential.getCoordinates(currentCoordinates);
272
273
274 Histogram histogram = orthogonalSpaceTempering.getHistogram();
275
276
277 double currentOSTEnergy = orthogonalSpaceTempering.energyAndGradient(currentCoordinates, gradient);
278
279
280 double currentdUdL = orthogonalSpaceTempering.getForceFielddEdL();
281 double currentForceFieldEnergy = orthogonalSpaceTempering.getForceFieldEnergy();
282 double currentBiasEnergy = orthogonalSpaceTempering.getBiasEnergy();
283
284
285 for (int imove = 0; imove < numMoves; imove++) {
286 long totalMoveTime = -nanoTime();
287
288
289 boolean lambdaBeforeMD = random.nextBoolean();
290
291 if (logger.isLoggable(Level.FINE)) {
292 if (equilibration) {
293 logger.fine(format("\n Equilibration Round %d", imove + 1));
294 } else {
295 String moveType = lambdaBeforeMD ? "Single-step lambda plus MD move." : "Single-step MD plus lambda move.";
296 logger.fine(format("\n MC-OST Round %d: %s", imove + 1, moveType));
297 }
298 logger.fine(format(" Starting force field energy for move %16.8f", currentForceFieldEnergy));
299 }
300
301
302 double currentLambda = orthogonalSpaceTempering.getLambda();
303 double proposedLambda;
304 if (equilibration) {
305
306 proposedLambda = currentLambda;
307 singleStepMD();
308 } else {
309 if (lambdaBeforeMD) {
310
311 proposedLambda = singleStepLambda();
312 singleStepMD();
313 } else {
314
315 singleStepMD();
316 proposedLambda = singleStepLambda();
317 }
318 }
319
320
321 double currentKineticEnergy = mdMove.getInitialKinetic();
322 double proposedKineticEnergy = mdMove.getKineticEnergy();
323
324
325 potential.getCoordinates(proposedCoordinates);
326
327
328 long proposedOSTEnergyTime = -nanoTime();
329 double proposedOSTEnergy;
330 try {
331 proposedOSTEnergy = orthogonalSpaceTempering.energyAndGradient(proposedCoordinates, gradient);
332 } catch (EnergyException e) {
333 mdMove.revertMove();
334 if (!equilibration) {
335 lambdaMove.revertMove();
336 lambda = currentLambda;
337 }
338 logger.log(Level.INFO, " Unstable MD Move skipped.");
339 continue;
340 }
341 proposedOSTEnergyTime += nanoTime();
342
343 if (logger.isLoggable(Level.FINE)) {
344 logger.fine(format(" Time to complete MD OST energy method call %6.3f", proposedOSTEnergyTime * NS2SEC));
345 }
346
347
348 double proposeddUdL = orthogonalSpaceTempering.getForceFielddEdL();
349 double proposedForceFieldEnergy = orthogonalSpaceTempering.getForceFieldEnergy();
350 double proposedBiasEnergy = orthogonalSpaceTempering.getBiasEnergy();
351
352
353 double currentTotalEnergy = currentOSTEnergy + currentKineticEnergy;
354 double proposedTotalEnergy = proposedOSTEnergy + proposedKineticEnergy;
355
356
357 if (logger.isLoggable(mcLogLevel)) {
358 logger.log(mcLogLevel, format("\n %8s %12s %12s %12s %12s", "", "Kinetic", "Potential", "Bias", "Total"));
359 logger.log(mcLogLevel, format(" Current %12.4f %12.4f %12.4f %12.4f", currentKineticEnergy,
360 currentForceFieldEnergy, currentBiasEnergy, currentTotalEnergy));
361 logger.log(mcLogLevel, format(" Proposed %12.4f %12.4f %12.4f %12.4f", proposedKineticEnergy,
362 proposedForceFieldEnergy, proposedBiasEnergy, proposedTotalEnergy));
363 logger.log(mcLogLevel, format(" Delta %12.4f %12.4f %12.4f %12.4f",
364 proposedKineticEnergy - currentKineticEnergy,
365 proposedForceFieldEnergy - currentForceFieldEnergy, proposedBiasEnergy - currentBiasEnergy,
366 proposedTotalEnergy - currentTotalEnergy));
367 }
368
369
370 double energyChange = mdMove.getEnergyChange();
371 if (abs(energyChange) > ENERGY_CONSERVATION_TOLERANCE) {
372 mdMove.revertMove();
373 if (!equilibration) {
374 lambdaMove.revertMove();
375 lambda = currentLambda;
376 }
377 logger.warning(" MC Move skipped due to lack of MD energy conservation.");
378 continue;
379 }
380
381
382 String result = " ";
383
384 double dE = proposedTotalEnergy - currentTotalEnergy;
385 if (equilibration) {
386 if (orthogonalSpaceTempering.insideHardWallConstraint(proposedLambda, proposeddUdL)
387 && evaluateMove(currentTotalEnergy, proposedTotalEnergy)) {
388
389 result = " -> ";
390 acceptMCOST++;
391 currentOSTEnergy = proposedOSTEnergy;
392 currentdUdL = proposeddUdL;
393 currentForceFieldEnergy = proposedForceFieldEnergy;
394 currentBiasEnergy = proposedBiasEnergy;
395 arraycopy(proposedCoordinates, 0, currentCoordinates, 0, n);
396 } else {
397 mdMove.revertMove();
398 }
399 } else {
400 if (orthogonalSpaceTempering.insideHardWallConstraint(proposedLambda, proposeddUdL)
401 && evaluateMove(currentTotalEnergy, proposedTotalEnergy)) {
402
403 result = " -> ";
404 acceptMCOST++;
405 lambda = proposedLambda;
406 currentdUdL = proposeddUdL;
407 currentForceFieldEnergy = proposedForceFieldEnergy;
408 arraycopy(proposedCoordinates, 0, currentCoordinates, 0, n);
409 } else {
410 lambdaMove.revertMove();
411 lambda = currentLambda;
412 mdMove.revertMove();
413 }
414 if (imove % biasDepositionFrequency == 0) {
415 histogram.addBias(currentdUdL);
416 if (logger.isLoggable(Level.FINE)) {
417 logger.fine(format(" Added Bias at move %d: [ L=%5.3f, FL=%9.3f] ", imove + 1, lambda, currentdUdL));
418 }
419 } else {
420 logger.info(format(" Cycle %d: skipping bias deposition.", imove + 1));
421 }
422
423
424 currentBiasEnergy = histogram.computeBiasEnergy(lambda, currentdUdL);
425
426
427 currentOSTEnergy = currentForceFieldEnergy + currentBiasEnergy;
428
429 boolean snapShot = lambda >= orthogonalSpaceTempering.getLambdaWriteOut();
430 long mdMoveNum = (imove + 1) * stepsPerMove;
431 orthogonalSpaceTempering.getHistogram().ld.stepsTaken += stepsPerMove;
432 EnumSet<MDWriteAction> written = mdMove.writeFilesForStep(mdMoveNum, snapShot, true);
433 if (written.contains(MDWriteAction.RESTART)) {
434 orthogonalSpaceTempering.writeAdditionalRestartInfo(false);
435 }
436 }
437
438
439 totalMoveTime += nanoTime();
440 double percent = (acceptMCOST * 100.0) / (imove + 1);
441 logger.info(format(" %4d [ L=%5.3f, dU/dL=%8.3f]%s[ L=%5.3f, dU/dL=%8.3f] ΔE=%12.4f (%5.1f%%) in %6.3f sec.",
442 imove + 1, currentLambda, currentdUdL, result,
443 proposedLambda, proposeddUdL, dE, percent, totalMoveTime * NS2SEC));
444 }
445 }
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461 public void sampleTwoStep() {
462
463 lambda = orthogonalSpaceTempering.getLambda();
464 lambda = lambdaMove.validateLambda(lambda);
465 orthogonalSpaceTempering.setLambda(lambda);
466
467 int n = potential.getNumberOfVariables();
468 double[] gradient = new double[n];
469 double[] currentCoordinates = new double[n];
470 double[] proposedCoordinates = new double[n];
471 long numMoves = totalSteps / stepsPerMove;
472 int acceptLambda = 0;
473 int acceptMD = 0;
474
475
476 potential.getCoordinates(currentCoordinates);
477
478
479 Histogram histogram = orthogonalSpaceTempering.getHistogram();
480
481
482 double currentOSTEnergy = orthogonalSpaceTempering.energyAndGradient(currentCoordinates, gradient);
483
484
485 double currentdUdL = orthogonalSpaceTempering.getForceFielddEdL();
486 double currentForceFieldEnergy = orthogonalSpaceTempering.getForceFieldEnergy();
487 double currentBiasEnergy = orthogonalSpaceTempering.getBiasEnergy();
488
489
490 for (int imove = 0; imove < numMoves; imove++) {
491 long totalMoveTime = -nanoTime();
492 long mdMoveAndEvalTime = -nanoTime();
493
494 if (logger.isLoggable(Level.FINE)) {
495 if (equilibration) {
496 logger.fine(format("\n MD Equilibration Round %d", imove + 1));
497 } else {
498 logger.fine(format("\n MC Orthogonal Space Sampling Round %d: Independent Steps", imove + 1));
499 }
500 }
501
502
503 long mdMoveTime = -nanoTime();
504 mdMove.move(mdVerbosity);
505 mdMoveTime += nanoTime();
506 logger.log(mcLogLevel, format(" Total time for MD move: %6.3f", mdMoveTime * NS2SEC));
507
508
509 double currentKineticEnergy = mdMove.getInitialKinetic();
510 double proposedKineticEnergy = mdMove.getKineticEnergy();
511
512
513 potential.getCoordinates(proposedCoordinates);
514
515
516 long proposedOSTEnergyTime = -nanoTime();
517
518 double proposedOSTEnergy;
519 try {
520 proposedOSTEnergy = orthogonalSpaceTempering.energyAndGradient(proposedCoordinates, gradient);
521 } catch (EnergyException e) {
522 mdMove.revertMove();
523 logger.log(Level.INFO, " Unstable MD Move skipped.");
524 continue;
525 }
526 proposedOSTEnergyTime += nanoTime();
527
528 logger.fine(format(" Time to complete MD OST energy method call %6.3f", proposedOSTEnergyTime * NS2SEC));
529
530
531 double proposeddUdL = orthogonalSpaceTempering.getForceFielddEdL();
532 double proposedForceFieldEnergy = orthogonalSpaceTempering.getForceFieldEnergy();
533 double proposedBiasEnergy = orthogonalSpaceTempering.getBiasEnergy();
534
535
536 double currentTotalEnergy = currentOSTEnergy + currentKineticEnergy;
537 double proposedTotalEnergy = proposedOSTEnergy + proposedKineticEnergy;
538
539 if (logger.isLoggable(mcLogLevel)) {
540 logger.log(mcLogLevel, format("\n %8s %12s %12s %12s %12s", "", "Kinetic", "Potential", "Bias", "Total"));
541 logger.log(mcLogLevel, format(" Current %12.4f %12.4f %12.4f %12.4f", currentKineticEnergy,
542 currentForceFieldEnergy, currentBiasEnergy, currentTotalEnergy));
543 logger.log(mcLogLevel, format(" Proposed %12.4f %12.4f %12.4f %12.4f", proposedKineticEnergy,
544 proposedForceFieldEnergy, proposedBiasEnergy, proposedTotalEnergy));
545 logger.log(mcLogLevel, format(" Delta %12.4f %12.4f %12.4f %12.4f",
546 proposedKineticEnergy - currentKineticEnergy,
547 proposedForceFieldEnergy - currentForceFieldEnergy, proposedBiasEnergy - currentBiasEnergy,
548 proposedTotalEnergy - currentTotalEnergy));
549 }
550
551 double energyChange = mdMove.getEnergyChange();
552 if (abs(energyChange) > ENERGY_CONSERVATION_TOLERANCE) {
553 mdMove.revertMove();
554 logger.warning(" MC Move skipped due to lack of MD energy conservation");
555 continue;
556 }
557
558 String result = " ";
559 double dE = proposedTotalEnergy - currentTotalEnergy;
560 if (orthogonalSpaceTempering.insideHardWallConstraint(orthogonalSpaceTempering.getLambda(),
561 proposeddUdL) && evaluateMove(currentTotalEnergy, proposedTotalEnergy)) {
562
563 acceptMD++;
564 result = " -> ";
565 currentOSTEnergy = proposedOSTEnergy;
566 currentdUdL = proposeddUdL;
567 currentForceFieldEnergy = proposedForceFieldEnergy;
568 currentBiasEnergy = proposedBiasEnergy;
569 arraycopy(proposedCoordinates, 0, currentCoordinates, 0, n);
570 } else {
571 mdMove.revertMove();
572 }
573
574
575 mdMoveAndEvalTime += nanoTime();
576 double percent = (acceptMD * 100.0) / (imove + 1);
577 logger.info(format(" %4d MD [ L=%5.3f, dU/dL=%8.3f]%s[ L=%5.3f, dU/dL=%8.3f] ΔE=%12.4f (%5.1f%%) in %6.3f sec.", imove + 1,
578 lambda, currentdUdL, result, lambda, proposeddUdL, dE, percent, mdMoveAndEvalTime * NS2SEC));
579
580
581 if (!equilibration) {
582
583 long lambdaMoveTime = -nanoTime();
584 double currentLambda = orthogonalSpaceTempering.getLambda();
585 lambdaMove.move();
586 double proposedLambda = orthogonalSpaceTempering.getLambda();
587
588
589 long proposedOSTEnergyTime2 = -nanoTime();
590 proposedOSTEnergy = orthogonalSpaceTempering.energyAndGradient(currentCoordinates, gradient);
591 proposedOSTEnergyTime2 += nanoTime();
592
593
594 proposedForceFieldEnergy = orthogonalSpaceTempering.getForceFieldEnergy();
595 proposeddUdL = orthogonalSpaceTempering.getForceFielddEdL();
596
597 if (logger.isLoggable(mcLogLevel)) {
598 logger.log(mcLogLevel, format("\n Time to complete Lambda OST energy method call %6.3f ", proposedOSTEnergyTime2 * NS2SEC));
599 logger.log(mcLogLevel, format(" Current OST %12.3f at L=%5.3f.", currentOSTEnergy, currentLambda));
600 logger.log(mcLogLevel, format(" Proposed OST %12.3f at L=%5.3f.", proposedOSTEnergy, proposedLambda));
601 logger.log(mcLogLevel, format(" MC Energy change: %12.3f (kcal/mol).", proposedOSTEnergy - currentOSTEnergy));
602 }
603
604 dE = proposedOSTEnergy - currentOSTEnergy;
605 if (orthogonalSpaceTempering.insideHardWallConstraint(proposedLambda, proposeddUdL)
606 && evaluateMove(currentOSTEnergy, proposedOSTEnergy)) {
607 acceptLambda++;
608 result = " -> ";
609 currentForceFieldEnergy = proposedForceFieldEnergy;
610 currentdUdL = proposeddUdL;
611 lambda = proposedLambda;
612 } else {
613 result = " ";
614 lambdaMove.revertMove();
615 lambda = currentLambda;
616 }
617
618 lambdaMoveTime += nanoTime();
619 percent = (acceptLambda * 100.0) / (imove + 1);
620 logger.info(format(" L [ L=%5.3f, dU/dL=%8.3f]%s[ L=%5.3f, dU/dL=%8.3f] ΔE=%12.4f (%5.1f%%) in %6.3f sec.",
621 currentLambda, currentdUdL, result, proposedLambda, proposeddUdL, dE, percent, lambdaMoveTime * NS2SEC));
622
623 if (imove % biasDepositionFrequency == 0) {
624 histogram.addBias(currentdUdL);
625 logger.fine(format(" Added Bias at move %d: [ L=%5.3f, FL=%9.3f] ", imove + 1, lambda, currentdUdL));
626 } else {
627 logger.info(format(" Cycle %d: skipping bias deposition.", imove + 1));
628 }
629
630
631 currentBiasEnergy = histogram.computeBiasEnergy(lambda, currentdUdL);
632
633
634
635 currentOSTEnergy = currentForceFieldEnergy + currentBiasEnergy;
636
637 if (automaticWriteouts) {
638 long mdMoveNum = (imove + 1) * stepsPerMove;
639 boolean trySnapshot = lambda >= orthogonalSpaceTempering.getLambdaWriteOut();
640 EnumSet<MDWriteAction> written = mdMove.writeFilesForStep(mdMoveNum, trySnapshot, true);
641 if (written.contains(MDWriteAction.RESTART)) {
642 orthogonalSpaceTempering.writeAdditionalRestartInfo(false);
643 }
644 }
645 }
646
647 if (logger.isLoggable(Level.FINE)) {
648 totalMoveTime += nanoTime();
649 logger.fine(format(" Round complete in %6.3f sec.", totalMoveTime * NS2SEC));
650 }
651 }
652 }
653
654 public void setAutomaticWriteouts(boolean autoWrite) {
655 automaticWriteouts = autoWrite;
656 }
657
658
659
660
661
662
663
664 public void setEquilibration(boolean equilibration) {
665 this.equilibration = equilibration;
666 }
667
668
669
670
671
672
673
674 public void setLambdaStdDev(double stdDev) {
675 if (!lambdaMove.isContinuous()) {
676 if (stdDev != orthogonalSpaceTempering.getHistogram().hd.lambdaBinWidth) {
677 logger.info(
678 format(" Requested Lambda step size change %6.4f is not equal to OST bin width %6.4f.",
679 stdDev, orthogonalSpaceTempering.getHistogram().hd.lambdaBinWidth));
680 return;
681 }
682 }
683 lambdaMove.setMoveSize(stdDev);
684 }
685
686
687
688
689
690
691 public void setTotalSteps(long totalSteps) {
692 this.totalSteps = totalSteps;
693 }
694
695
696
697
698
699
700 private double singleStepLambda() {
701 lambdaMove.move();
702 double proposedLambda = orthogonalSpaceTempering.getLambda();
703 logger.log(mcLogLevel, format(" Proposed lambda: %5.3f.", proposedLambda));
704 return proposedLambda;
705 }
706
707
708
709
710 private void singleStepMD() {
711 long mdMoveTime = -nanoTime();
712 mdMove.move(mdVerbosity);
713 mdMoveTime += nanoTime();
714 logger.log(mcLogLevel, format(" Total time for MD move: %6.3f", mdMoveTime * NS2SEC));
715 }
716
717
718
719
720 @Override
721 public void revertStep() {
722 throw new UnsupportedOperationException("Not supported yet.");
723 }
724
725
726
727
728 @Override
729 protected double currentEnergy() {
730 throw new UnsupportedOperationException("Not supported yet.");
731 }
732
733
734
735
736 @Override
737 protected void storeState() {
738 throw new UnsupportedOperationException("Not supported yet.");
739 }
740 }