Line data Source code
1 : //* This file is part of the MOOSE framework 2 : //* https://mooseframework.inl.gov 3 : //* 4 : //* All rights reserved, see COPYRIGHT for full restrictions 5 : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT 6 : //* 7 : //* Licensed under LGPL 2.1, please see LICENSE for details 8 : //* https://www.gnu.org/licenses/lgpl-2.1.html 9 : 10 : #include "ParallelSubsetSimulation.h" 11 : #include "AdaptiveMonteCarloUtils.h" 12 : #include "Normal.h" 13 : #include "Uniform.h" 14 : 15 : registerMooseObject("StochasticToolsApp", ParallelSubsetSimulation); 16 : 17 : InputParameters 18 40 : ParallelSubsetSimulation::validParams() 19 : { 20 40 : InputParameters params = Sampler::validParams(); 21 40 : params.addClassDescription("Parallel Subset Simulation sampler."); 22 80 : params.addRequiredParam<std::vector<DistributionName>>( 23 : "distributions", 24 : "The distribution names to be sampled, the number of distributions provided defines the " 25 : "number of columns per matrix."); 26 80 : params.addRequiredParam<ReporterName>("output_reporter", 27 : "Reporter with results of samples created by the SubApp."); 28 80 : params.addRequiredParam<ReporterName>("inputs_reporter", "Reporter with input parameters."); 29 120 : params.addRangeCheckedParam<Real>("subset_probability", 30 80 : 0.1, 31 : "subset_probability>0 & subset_probability<=1", 32 : "Conditional probability of each subset"); 33 80 : params.addRequiredParam<unsigned int>("num_samplessub", "Number of samples per subset"); 34 80 : params.addRequiredParam<unsigned int>("num_subsets", "Number of desired subsets"); 35 80 : params.addParam<unsigned int>("num_parallel_chains", 36 : "Number of Markov chains to run in parallel, default is based on " 37 : "the number of processors used."); 38 80 : params.addParam<bool>("use_absolute_value", false, "Use absolute value of the sub app output"); 39 80 : params.addParam<unsigned int>( 40 : "num_random_seeds", 41 80 : 100000, 42 : "Initialize a certain number of random seeds. Change from the default only if you have to."); 43 40 : return params; 44 0 : } 45 : 46 23 : ParallelSubsetSimulation::ParallelSubsetSimulation(const InputParameters & parameters) 47 : : Sampler(parameters), 48 23 : _num_samplessub(getParam<unsigned int>("num_samplessub")), 49 46 : _num_subsets(getParam<unsigned int>("num_subsets")), 50 46 : _use_absolute_value(getParam<bool>("use_absolute_value")), 51 46 : _subset_probability(getParam<Real>("subset_probability")), 52 46 : _num_random_seeds(getParam<unsigned int>("num_random_seeds")), 53 23 : _outputs(getReporterValue<std::vector<Real>>("output_reporter")), 54 23 : _inputs(getReporterValue<std::vector<std::vector<Real>>>("inputs_reporter")), 55 46 : _step(getCheckedPointerParam<FEProblemBase *>("_fe_problem_base")->timeStep()), 56 23 : _count_max(std::floor(1 / _subset_probability)), 57 23 : _check_step(0), 58 23 : _subset(0), 59 23 : _is_sampling_completed(false) 60 : { 61 : // Fixing the number of rows to the number of processors 62 23 : const dof_id_type nchains = isParamValid("num_parallel_chains") 63 69 : ? getParam<unsigned int>("num_parallel_chains") 64 0 : : n_processors() / _min_procs_per_row; 65 23 : setNumberOfRows(nchains); 66 23 : if ((_num_samplessub / nchains) % _count_max > 0) 67 0 : mooseError("Number of model evaluations per chain per subset (", 68 0 : _num_samplessub / nchains, 69 : ") should be a multiple of requested chain length (", 70 0 : _count_max, 71 : ")."); 72 : 73 : // Filling the `distributions` vector with the user-provided distributions. 74 92 : for (const DistributionName & name : getParam<std::vector<DistributionName>>("distributions")) 75 46 : _distributions.push_back(&getDistributionByName(name)); 76 : 77 : // Setting the number of columns in the sampler matrix (equal to the number of distributions). 78 23 : setNumberOfCols(_distributions.size()); 79 : 80 : /* `inputs_sto` is a member variable that aids in deciding the next set of samples 81 : in the Subset Simulation algorithm by storing the input parameter values*/ 82 23 : _inputs_sto.resize(_distributions.size(), std::vector<Real>(_num_samplessub, 0.0)); 83 23 : _outputs_sto.resize(_num_samplessub, 0.0); 84 : 85 : /* `inputs_sorted` is a member variable which also aids in deciding the next set of samples 86 : in the Subset Simulation algorithm by storing the sorted input parameter values 87 : by their corresponding output values*/ 88 23 : _inputs_sorted.resize(_distributions.size()); 89 : 90 : /* `markov_seed` is a member variable to store the seed input values for proposing 91 : the next set of Markov chain samples.*/ 92 23 : _markov_seed.resize(_distributions.size()); 93 : 94 23 : setNumberOfRandomSeeds(_num_random_seeds); 95 23 : } 96 : 97 : const unsigned int & 98 306 : ParallelSubsetSimulation::getNumSamplesSub() const 99 : { 100 306 : return _num_samplessub; 101 : } 102 : 103 : const bool & 104 102 : ParallelSubsetSimulation::getUseAbsoluteValue() const 105 : { 106 102 : return _use_absolute_value; 107 : } 108 : 109 : const Real & 110 170 : ParallelSubsetSimulation::getSubsetProbability() const 111 : { 112 170 : return _subset_probability; 113 : } 114 : 115 : void 116 204 : ParallelSubsetSimulation::sampleSetUp(const SampleMode mode) 117 : { 118 204 : if (_step <= 1 || _check_step == _step) 119 119 : return; 120 85 : _check_step = _step; 121 : 122 85 : if (_is_sampling_completed) 123 0 : mooseError("Internal bug: the adaptive sampling is supposed to be completed but another sample " 124 : "has been requested."); 125 : 126 85 : _subset = ((_step - 1) * getNumberOfRows()) / _num_samplessub; 127 85 : const unsigned int sub_ind = (_step - 1) - (_num_samplessub / getNumberOfRows()) * _subset; 128 85 : const unsigned int offset = sub_ind * getNumberOfRows(); 129 : 130 : // Get and store the accepted samples input across all the procs from the previous step 131 255 : for (dof_id_type j = 0; j < _distributions.size(); ++j) 132 510 : for (dof_id_type ss = 0; ss < getNumberOfRows(); ++ss) 133 340 : _inputs_sto[j][ss + offset] = Normal::quantile(_distributions[j]->cdf(_inputs[j][ss]), 0, 1); 134 : 135 : // Get the accepted sample outputs across all the procs from the previous step 136 : std::vector<Real> tmp = 137 85 : _use_absolute_value ? AdaptiveMonteCarloUtils::computeVectorABS(_outputs) : _outputs; 138 85 : if (mode == Sampler::SampleMode::GLOBAL) 139 0 : _communicator.allgather(tmp); 140 : else 141 85 : _local_comm.allgather(tmp); 142 255 : for (dof_id_type ss = 0; ss < getNumberOfRows(); ++ss) 143 170 : _outputs_sto[ss + offset] = tmp[ss]; 144 : 145 : // These are the subsequent subsets which use Markov Chain Monte Carlo sampling scheme 146 85 : if (_subset > 0) 147 : { 148 : // Check whether the subset index has changed 149 68 : if (sub_ind == 0) 150 : { 151 : // _inputs_sorted contains the input values corresponding to the largest po percentile 152 : // output values 153 34 : _inputs_sorted = AdaptiveMonteCarloUtils::sortInput( 154 34 : _inputs_sto, _outputs_sto, _num_samplessub, _subset_probability); 155 : } 156 : 157 : // Reinitialize the starting inputs values for the next set of Markov chains 158 68 : if (sub_ind % _count_max == 0) 159 : { 160 34 : const unsigned int soffset = (sub_ind / _count_max) * getNumberOfRows(); 161 102 : for (dof_id_type j = 0; j < _distributions.size(); ++j) 162 136 : _markov_seed[j].assign(_inputs_sorted[j].begin() + soffset + getLocalRowBegin(), 163 68 : _inputs_sorted[j].begin() + soffset + getLocalRowEnd()); 164 : } 165 : // Otherwise, use the previously accepted input values to propose the next set of input 166 : // values 167 : else 168 : { 169 102 : for (dof_id_type j = 0; j < _distributions.size(); ++j) 170 136 : _markov_seed[j].assign(_inputs_sto[j].begin() + offset + getLocalRowBegin(), 171 68 : _inputs_sto[j].begin() + offset + getLocalRowEnd()); 172 : } 173 : } 174 : 175 : // check if we have completed the last sample (sub_ind == _num_samplessub /getNumberOfRows() - 1) 176 : // of the last subset (_subset == _num_subsets - 1) 177 85 : if (_subset == _num_subsets - 1 && sub_ind == _num_samplessub / getNumberOfRows() - 1) 178 17 : _is_sampling_completed = true; 179 85 : } 180 : 181 : Real 182 528 : ParallelSubsetSimulation::computeSample(dof_id_type row_index, dof_id_type col_index) 183 : { 184 528 : unsigned int seed_value = _step > 0 ? (_step - 1) * 2 : 0; 185 : Real val; 186 : 187 528 : if (_subset == 0) 188 176 : val = getRand(seed_value); 189 : else 190 : { 191 352 : const dof_id_type loc_ind = row_index - getLocalRowBegin(); 192 352 : const Real rv = Normal::quantile(getRand(seed_value), _markov_seed[col_index][loc_ind], 1.0); 193 352 : const Real acceptance_ratio = std::log(Normal::pdf(rv, 0, 1)) - 194 352 : std::log(Normal::pdf(_markov_seed[col_index][loc_ind], 0, 1)); 195 352 : const Real new_sample = acceptance_ratio > std::log(getRand(seed_value + 1)) 196 352 : ? rv 197 352 : : _markov_seed[col_index][loc_ind]; 198 352 : val = Normal::cdf(new_sample, 0, 1); 199 : } 200 : 201 528 : return _distributions[col_index]->quantile(val); 202 : }