diff --git a/developer_tools/XSDSchemas/Samplers.xsd b/developer_tools/XSDSchemas/Samplers.xsd index 884fadcaa9..e495471746 100644 --- a/developer_tools/XSDSchemas/Samplers.xsd +++ b/developer_tools/XSDSchemas/Samplers.xsd @@ -398,6 +398,7 @@ + diff --git a/framework/JobHandler.py b/framework/JobHandler.py index 6ff806bc96..b82e62b245 100644 --- a/framework/JobHandler.py +++ b/framework/JobHandler.py @@ -377,6 +377,18 @@ def addClientJob(self, args, functionToRun, identifier, metadata=None, uniqueHan forceUseThreads = True, uniqueHandler = uniqueHandler, clientQueue = True) + def addFinishedJob(self, data): + """ + Takes an already-finished job (for example, a restart realization) and adds it to the finished queue. + @ In, data, dict, completed realization + @ Out, None + """ + # create a placeholder runner + run = Runners.PassthroughRunner(self.messageHandler, data) + # place it on the finished queue + with self.__queueLock: + self.__finished.append(run) + def isFinished(self): """ Method to check if all the runs in the queue are finished diff --git a/framework/Runners/PassthroughRunner.py b/framework/Runners/PassthroughRunner.py new file mode 100644 index 0000000000..9c6da2e44d --- /dev/null +++ b/framework/Runners/PassthroughRunner.py @@ -0,0 +1,86 @@ +# Copyright 2017 Battelle Energy Alliance, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +""" + Module for Passthrough Runner class, which skips evaluation. Used particularly + for restarting Samplers from existing data currently. +""" +import numpy as np +from .Runner import Runner + +class PassthroughRunner(Runner): + """ + A runner for when we already have the answer, but need to go through the mechanics. + """ + def __init__(self, messageHandler, data, metadata=None, uniqueHandler="any", profile=False): + """ + Init method + @ In, messageHandler, MessageHandler object, the global RAVEN message + handler object + @ In, data, dict, fully-evaluated realization + @ In, metadata, dict, optional, dictionary of metadata associated with + this run + @ In, uniqueHandler, string, optional, it is a special keyword attached to + this runner. For example, if present, to retrieve this runner using the + method jobHandler.getFinished, the uniqueHandler needs to be provided. + If uniqueHandler == 'any', every "client" can get this runner + @ In, profile, bool, optional, if True then at deconstruction timing statements will be printed + @ Out, None + """ + super(PassthroughRunner, self).__init__(messageHandler, metadata=metadata, uniqueHandler=uniqueHandler, profile=profile) + self._data = data # realization with completed data + self.returnCode = 0 # passthrough was born successful + + def isDone(self): + """ + Method to check if the calculation associated with this Runner is finished + @ In, None + @ Out, isDone, bool, is it finished? + """ + return True # passthrough was born done + + def getReturnCode(self): + """ + Returns the return code from "running the code." + @ In, None + @ Out, returnCode, int, the return code of this evaluation + """ + return self.returnCode + + def getEvaluation(self): + """ + Return solution. + @ In, None + @ Out, result, dict, results + """ + result = {} + result.update(dict((key, np.atleast_1d(value)) for key, value in self._data['inputs'].items())) + result.update(dict((key, np.atleast_1d(value)) for key, value in self._data['outputs'].items())) + result.update(dict((key, np.atleast_1d(value)) for key, value in self._data['metadata'].items())) + return result + + def start(self): + """ + Method to start the job associated to this Runner + @ In, None + @ Out, None + """ + pass # passthrough was born done + + def kill(self): + """ + Method to kill the job associated to this Runner + @ In, None + @ Out, None + """ + pass # passthrough was born done; you can't kill it \ No newline at end of file diff --git a/framework/Runners/Runner.py b/framework/Runners/Runner.py index 5a9d989038..2a8c7d50e3 100644 --- a/framework/Runners/Runner.py +++ b/framework/Runners/Runner.py @@ -38,7 +38,7 @@ class Runner(MessageHandler.MessageUser): Generic base class for running codes and models in parallel environments both internally (shared data) and externally. """ - def __init__(self, messageHandler, identifier = None, metadata = None, uniqueHandler = "any", profile = False): + def __init__(self, messageHandler, identifier=None, metadata=None, uniqueHandler="any", profile=False): """ Initialize command variable @ In, messageHandler, MessageHandler instance, the global RAVEN message handler instance diff --git a/framework/Runners/__init__.py b/framework/Runners/__init__.py index da0a470f45..d95a77cc71 100644 --- a/framework/Runners/__init__.py +++ b/framework/Runners/__init__.py @@ -28,6 +28,7 @@ from .InternalRunner import InternalRunner from .SharedMemoryRunner import SharedMemoryRunner from .DistributedMemoryRunner import DistributedMemoryRunner +from .PassthroughRunner import PassthroughRunner from .Error import Error # from .Factory import knownTypes diff --git a/framework/Steps.py b/framework/Steps.py index 0ac9ecb56d..05bc3263ae 100644 --- a/framework/Steps.py +++ b/framework/Steps.py @@ -626,9 +626,10 @@ def _localInitializeStep(self,inDictionary): for inputIndex in range(inDictionary['jobHandler'].runInfoDict['batchSize']): if inDictionary[self.samplerType].amIreadyToProvideAnInput(): try: - newInput = self._findANewInputToRun(inDictionary[self.samplerType], inDictionary['Model'], inDictionary['Input'], inDictionary['Output']) - inDictionary["Model"].submit(newInput, inDictionary[self.samplerType].type, inDictionary['jobHandler'], **copy.deepcopy(inDictionary[self.samplerType].inputInfo)) - self.raiseADebug('Submitted input '+str(inputIndex+1)) + newInput = self._findANewInputToRun(inDictionary[self.samplerType], inDictionary['Model'], inDictionary['Input'], inDictionary['Output'], inDictionary['jobHandler']) + if newInput is not None: + inDictionary["Model"].submit(newInput, inDictionary[self.samplerType].type, inDictionary['jobHandler'], **copy.deepcopy(inDictionary[self.samplerType].inputInfo)) + self.raiseADebug('Submitted input '+str(inputIndex+1)) except utils.NoMoreSamplesNeeded: self.raiseAMessage('Sampler returned "NoMoreSamplesNeeded". Continuing...') @@ -710,8 +711,9 @@ def _localTakeAstepRun(self,inDictionary): if sampler.amIreadyToProvideAnInput(): try: - newInput = self._findANewInputToRun(sampler, model, inputs, outputs) - model.submit(newInput, inDictionary[self.samplerType].type, jobHandler, **copy.deepcopy(sampler.inputInfo)) + newInput = self._findANewInputToRun(sampler, model, inputs, outputs, jobHandler) + if newInput is not None: + model.submit(newInput, inDictionary[self.samplerType].type, jobHandler, **copy.deepcopy(sampler.inputInfo)) except utils.NoMoreSamplesNeeded: self.raiseAMessage(' ... Sampler returned "NoMoreSamplesNeeded". Continuing...') break @@ -728,7 +730,7 @@ def _localTakeAstepRun(self,inDictionary): # if any collected runs failed, let the sampler treat them appropriately, and any other closing-out actions sampler.finalizeSampler(self.failedRuns) - def _findANewInputToRun(self, sampler, model, inputs, outputs): + def _findANewInputToRun(self, sampler, model, inputs, outputs, jobHandler): """ Repeatedly calls Sampler until a new run is found or "NoMoreSamplesNeeded" is raised. @ In, sampler, Sampler, the sampler in charge of generating the sample @@ -741,18 +743,20 @@ def _findANewInputToRun(self, sampler, model, inputs, outputs): (i.e., a DataObject, File, or HDF5, I guess? Maybe these should all inherit from some base "Data" so that we can ensure a consistent interface for these?) - @ Out, newInp, list, list containing the new inputs + @ In, jobHandler, object, the raven object used to handle jobs + @ Out, newInp, list, list containing the new inputs (or None if a restart) """ #The value of "found" determines what the Sampler is ready to provide. # case 0: a new sample has been discovered and can be run, and newInp is a new input list. # case 1: found the input in restart, and newInp is a realization dicitonary of data to use - found = None - while found != 0: - found, newInp = sampler.generateInput(model,inputs) - if found == 1: - # loop over the outputs for this step and collect the data for each - for collector, outIndex in self._outputDictCollectionLambda: - collector([newInp,outputs[outIndex]]) + found, newInp = sampler.generateInput(model,inputs) + if found == 1: + # "submit" the finished run + jobHandler.addFinishedJob(newInp) + return None + # NOTE: we return None here only because the Sampler's "counter" is not correctly passed + # through if we add several samples at once through the restart. If we actually returned + # a Realization object from the Sampler, this would not be a problem. - talbpaul return newInp # # diff --git a/tests/framework/Samplers/Restart/adaptive_mc.xml b/tests/framework/Samplers/Restart/adaptive_mc.xml new file mode 100644 index 0000000000..d4a23c21c8 --- /dev/null +++ b/tests/framework/Samplers/Restart/adaptive_mc.xml @@ -0,0 +1,197 @@ + + + + framework/Samplers/Restart.AMC + talbpaul + 2020-07-16 + Samplers.AdaptiveMonteCarlo + + Tests restarting an Adaptive Monte Carlo sampling from restart. \texttt{makeCoarse} samples initial data, then \texttt{makeRestart} + makes additional samples, restarting from the first set of samples. \texttt{makeFine} does all the samples without restart + for comparison. The model for \texttt{coarse} always returns a value of 1, while the model for \texttt{restart} returns a value of 2, so + you can tell which samples came from which sampling strategy. Further, the solution export for + the restart should contain all the sample points, not merely the newly-sampled points. + + + + + AMC + makeCoarse,makeRestart,makeFine,print + + + + + dummyIN + coarsemod + coarse + exportCoarse + solns + + + dummyIN + finemod + fine + exportFine + solnsFine + + + solns + finemod + restart + exportRestart + solnsRestart + + + solns + solnsFine + solnsRestart + exportCoarse + exportFine + exportRestart + coarse + fine + restart + exp_coarse + exp_fine + exp_restart + + + + + + 1 + 2 + + + 2 + 3 + + + + + + solns + + 50 + 5 + False + x1 + + 42 + + u1 + + + u2 + + + + solnsRestart + + 50 + 5 + False + x1 + + 42 + + u1 + + + u2 + + solns + + + solnsFine + + 50 + 5 + False + x1 + + 42 + + u1 + + + u2 + + + + + + + + x1,x2,ans + + + x1,x2,ans + + + + + + x1,x2 + OutputPlaceHolder + + + x1,x2 + ans + + + x1,x2 + ans + + + x1,x2 + ans + + + solutionUpdate + mean_x1,mean_ste_x1 + + + solutionUpdate + mean_x1,mean_ste_x1 + + + solutionUpdate + mean_x1,mean_ste_x1 + + + + + + csv + solns + input,output + + + csv + solnsFine + input,output + + + csv + solnsRestart + input,output + + + csv + exportCoarse + input,output + + + csv + exportFine + input,output + + + csv + exportRestart + input,output + + + + diff --git a/tests/framework/Samplers/Restart/gold/AMC/coarse.csv b/tests/framework/Samplers/Restart/gold/AMC/coarse.csv new file mode 100644 index 0000000000..d59b9318c6 --- /dev/null +++ b/tests/framework/Samplers/Restart/gold/AMC/coarse.csv @@ -0,0 +1,7 @@ +x1,x2,ans +1.3745401144,2.79654298439,1.0 +1.95071431178,2.18343478771,1.0 +1.7319939385,2.77969099762,1.0 +1.59865848641,2.59685016158,1.0 +1.15601863855,2.44583275762,1.0 +1.15599452382,2.09997492053,1.0 diff --git a/tests/framework/Samplers/Restart/gold/AMC/exp_coarse.csv b/tests/framework/Samplers/Restart/gold/AMC/exp_coarse.csv new file mode 100644 index 0000000000..9547170773 --- /dev/null +++ b/tests/framework/Samplers/Restart/gold/AMC/exp_coarse.csv @@ -0,0 +1,6 @@ +solutionUpdate,mean_x1,mean_ste_x1 +1,1.66262721309,0.288087098693 +2,1.68574945489,0.167926661206 +3,1.66397671277,0.120721721622 +4,1.56238509793,0.138076415978 +5,1.49465333558,0.131520553771 diff --git a/tests/framework/Samplers/Restart/gold/AMC/exp_fine.csv b/tests/framework/Samplers/Restart/gold/AMC/exp_fine.csv new file mode 100644 index 0000000000..176ceb43db --- /dev/null +++ b/tests/framework/Samplers/Restart/gold/AMC/exp_fine.csv @@ -0,0 +1,16 @@ +solutionUpdate,mean_x1,mean_ste_x1 +1,1.66262721309,0.288087098693 +2,1.68574945489,0.167926661206 +3,1.66397671277,0.120721721622 +4,1.56238509793,0.138076415978 +5,1.49465333558,0.131520553771 +6,1.43228623208,0.127456362007 +7,1.48652247167,0.122985417215 +8,1.4992549761,0.109207722536 +9,1.52013673634,0.0998854806255 +10,1.47472289656,0.101121245154 +11,1.51598847578,0.101114307658 +12,1.54033110353,0.0961443070765 +13,1.51690310413,0.092043828684 +14,1.49456456166,0.0885520639248 +15,1.47511705843,0.0850851920379 diff --git a/tests/framework/Samplers/Restart/gold/AMC/exp_restart.csv b/tests/framework/Samplers/Restart/gold/AMC/exp_restart.csv new file mode 100644 index 0000000000..176ceb43db --- /dev/null +++ b/tests/framework/Samplers/Restart/gold/AMC/exp_restart.csv @@ -0,0 +1,16 @@ +solutionUpdate,mean_x1,mean_ste_x1 +1,1.66262721309,0.288087098693 +2,1.68574945489,0.167926661206 +3,1.66397671277,0.120721721622 +4,1.56238509793,0.138076415978 +5,1.49465333558,0.131520553771 +6,1.43228623208,0.127456362007 +7,1.48652247167,0.122985417215 +8,1.4992549761,0.109207722536 +9,1.52013673634,0.0998854806255 +10,1.47472289656,0.101121245154 +11,1.51598847578,0.101114307658 +12,1.54033110353,0.0961443070765 +13,1.51690310413,0.092043828684 +14,1.49456456166,0.0885520639248 +15,1.47511705843,0.0850851920379 diff --git a/tests/framework/Samplers/Restart/gold/AMC/fine.csv b/tests/framework/Samplers/Restart/gold/AMC/fine.csv new file mode 100644 index 0000000000..8d17e9e9cd --- /dev/null +++ b/tests/framework/Samplers/Restart/gold/AMC/fine.csv @@ -0,0 +1,17 @@ +x1,x2,ans +1.3745401144,2.79654298439,2.0 +1.95071431178,2.18343478771,2.0 +1.7319939385,2.77969099762,2.0 +1.59865848641,2.59685016158,2.0 +1.15601863855,2.44583275762,2.0 +1.15599452382,2.09997492053,2.0 +1.05808361109,2.45924888795,2.0 +1.86617614885,2.33370861139,2.0 +1.60111501152,2.14286681431,2.0 +1.70807257847,2.65088847341,2.0 +1.02058449877,2.05641157647,2.0 +1.96990984724,2.72199877159,2.0 +1.83244263656,2.9385527144,2.0 +1.21233911189,2.00077876472,2.0 +1.18182496707,2.99221156444,2.0 +1.18340450995,2.6174815075,2.0 diff --git a/tests/framework/Samplers/Restart/gold/AMC/restart.csv b/tests/framework/Samplers/Restart/gold/AMC/restart.csv new file mode 100644 index 0000000000..533255eabd --- /dev/null +++ b/tests/framework/Samplers/Restart/gold/AMC/restart.csv @@ -0,0 +1,17 @@ +x1,x2,ans +1.3745401144,2.79654298439,1.0 +1.95071431178,2.18343478771,1.0 +1.7319939385,2.77969099762,1.0 +1.59865848641,2.59685016158,1.0 +1.15601863855,2.44583275762,1.0 +1.15599452382,2.09997492053,1.0 +1.05808361109,2.45924888795,2.0 +1.86617614885,2.33370861139,2.0 +1.60111501152,2.14286681431,2.0 +1.70807257847,2.65088847341,2.0 +1.02058449877,2.05641157647,2.0 +1.96990984724,2.72199877159,2.0 +1.83244263656,2.9385527144,2.0 +1.21233911189,2.00077876472,2.0 +1.18182496707,2.99221156444,2.0 +1.18340450995,2.6174815075,2.0 diff --git a/tests/framework/Samplers/Restart/tests b/tests/framework/Samplers/Restart/tests index 8503749034..e8564edfda 100644 --- a/tests/framework/Samplers/Restart/tests +++ b/tests/framework/Samplers/Restart/tests @@ -7,6 +7,12 @@ UnorderedCsv = 'mc/coarse.csv mc/restart.csv mc/fine.csv' [../] + [./AMC] + type = 'RavenFramework' + input = 'adaptive_mc.xml' + UnorderedCsv = 'AMC/coarse.csv AMC/restart.csv AMC/fine.csv AMC/exp_coarse.csv AMC/exp_restart.csv AMC/exp_fine.csv' + [../] + [./StochPoly] type = 'RavenFramework' input = 'test_restart_stochpoly.xml' @@ -40,7 +46,7 @@ output = 'large/large_restart.xml' UnorderedCsv = 'large/large_restart.csv' [../] - + [./Constant] type = 'RavenFramework' input = 'test_restart_constant.xml'