1 /*  _______________________________________________________________________
2 
3     DAKOTA: Design Analysis Kit for Optimization and Terascale Applications
4     Copyright 2014-2020 National Technology & Engineering Solutions of Sandia, LLC (NTESS).
5     This software is distributed under the GNU Lesser General Public License.
6     For more information, see the README file in the top Dakota directory.
7     _______________________________________________________________________ */
8 
9 #include "RandomFieldModel.hpp"
10 #include "MarginalsCorrDistribution.hpp"
11 #include "ParallelLibrary.hpp"
12 #include "Teuchos_SerialDenseHelpers.hpp"
13 
14 namespace Dakota {
15 
16 /// initialization of static needed by RecastModel
17 RandomFieldModel* RandomFieldModel::rfmInstance(NULL);
18 
19 
RandomFieldModel(ProblemDescDB & problem_db)20 RandomFieldModel::RandomFieldModel(ProblemDescDB& problem_db):
21   RecastModel(problem_db, get_sub_model(problem_db)),
22   // LPS TODO: initialize other class data members off problemDB
23   numObservations(0),
24   expansionForm(problem_db.get_ushort("model.rf.expansion_form")),
25   covarianceForm(problem_db.get_ushort("model.rf.analytic_covariance")),
26   requestedReducedRank(problem_db.get_int("model.rf.expansion_bases")),
27   percentVariance(problem_db.get_real("model.truncation_tolerance")),
28   actualReducedRank(5)
29 {
30   modelType = "random_field";
31   modelId = RecastModel::recast_model_id(root_model_id(), "RANDOM_FIELD");
32   init_dace_iterator(problem_db);
33 
34   validate_inputs();
35 }
36 
37 
~RandomFieldModel()38 RandomFieldModel::~RandomFieldModel()
39 {  /* empty dtor */  }
40 
41 
get_sub_model(ProblemDescDB & problem_db)42 Model RandomFieldModel::get_sub_model(ProblemDescDB& problem_db)
43 {
44   Model sub_model;
45 
46   const String& propagation_model_pointer
47     = problem_db.get_string("model.rf.propagation_model_pointer");
48   size_t model_index = problem_db.get_db_model_node(); // for restoration
49   problem_db.set_db_model_nodes(propagation_model_pointer);
50   sub_model = problem_db.get_model();
51   //check_submodel_compatibility(actualModel);
52   problem_db.set_db_model_nodes(model_index); // restore
53 
54   return sub_model;
55 }
56 
57 
init_dace_iterator(ProblemDescDB & problem_db)58 void RandomFieldModel::init_dace_iterator(ProblemDescDB& problem_db)
59 {
60   const String& dace_method_pointer
61     = problem_db.get_string("model.dace_method_pointer");
62 
63   if (!dace_method_pointer.empty()) { // global DACE approximations
64     size_t method_index = problem_db.get_db_method_node(); // for restoration
65     size_t model_index  = problem_db.get_db_model_node();  // for restoration
66     problem_db.set_db_list_nodes(dace_method_pointer);
67 
68     // instantiate the DACE iterator, which instantiates the actual model
69     daceIterator = problem_db.get_iterator();
70     daceIterator.sub_iterator_flag(true);
71 
72     // retrieve the actual model from daceIterator (invalid for selected
73     // meta-iterators, e.g., hybrids)
74     Model& rf_generating_model = daceIterator.iterated_model();
75     // BMA TODO: review
76     //    check_submodel_compatibility(actualModel);
77     // if outer level output is verbose/debug and actualModel verbosity is
78     // defined by the DACE method spec, request fine-grained evaluation
79     // reporting for purposes of the final output summary.  This allows verbose
80     // final summaries without verbose output on every dace-iterator completion.
81     if (outputLevel > NORMAL_OUTPUT)
82       rf_generating_model.fine_grained_evaluation_counters();
83 
84     problem_db.set_db_method_node(method_index); // restore method only
85     problem_db.set_db_model_nodes(model_index);  // restore all model nodes
86 
87 
88     // TODO: may want to make this a quieter, lighter iterator?
89     daceIterator.sub_iterator_flag(true);
90   }
91 }
92 
93 
validate_inputs()94 void RandomFieldModel::validate_inputs()
95 {
96   //if (buildField) {
97   if (rfDataFilename.empty() && daceIterator.is_null() && (covarianceForm == NOCOVAR)) {
98     Cerr << "\nError: Random field model requires data_file or "
99 	 << "dace_method_pointer or specification of an analytic covariance"
100          << std::endl;
101     abort_handler(MODEL_ERROR);
102   }
103 
104   //  }
105 }
106 
107 
108 /** May eventually take on init_comms and related operations.  Also
109     may want ide of build/update like DataFitSurrModel, eventually. */
initialize_mapping(ParLevLIter pl_iter)110 bool RandomFieldModel::initialize_mapping(ParLevLIter pl_iter)
111 {
112   bool sub_model_resize = RecastModel::initialize_mapping(pl_iter);
113 
114   // TODO: create modes to switch between generating, accepting, and
115   // underlying model
116   fieldRealizationId=0;
117 
118   // runtime operation to identify the random field approximation
119   if (covarianceForm != NOCOVAR) {
120     rf_suite_identify_field_model();
121     expansionForm = RF_KARHUNEN_LOEVE;
122   }
123   else {
124     get_field_data();
125     identify_field_model();
126   }
127 
128   // complete initialization of the base RecastModel
129   initialize_recast();
130 
131   if (expansionForm == RF_KARHUNEN_LOEVE) {
132     // augment mvDist with normal(0,1)
133     initialize_rf_coeffs();
134     // update message lengths for send/receive of parallel jobs (normally
135     // performed once in Model::init_communicators() just after construct time)
136     estimate_message_lengths();
137 
138     return true; // size of variables changed
139   }
140   // may need true for PCA/GP if the vars characterization changes
141   return false; // size of variables unchanged
142 }
143 
144 
145 /** Populate rfBuildData */
get_field_data()146 void RandomFieldModel::get_field_data()
147 {
148   // TODO: either load the data matrix using ReducedBasis utilities or
149   // run daceIterator
150 
151   if (daceIterator.is_null()) {
152     // TODO: temporary data for testing
153     std::ifstream rf_file;
154     rf_file.open("rfbuild.test");
155     RealVectorArray va;
156     read_sized_data(rf_file, va, 5, 50);
157     rfBuildData.reshape(5,50);
158     copy_data(va,rfBuildData);
159   }
160   else {
161     // generate samples of the random field
162     Cout << "\nRandomFieldModel: Gathering random field data from RF-generating "
163          << "model" << std::endl;
164     daceIterator.run();
165     size_t num_samples = daceIterator.num_samples();
166     // BMA TODO: relax assumption of allSamples / compactMode
167     // Generalize to discrete vars
168     if (expansionForm == RF_PCA_GP) {
169       rfBuildVars.reshape(subModel.cv(), num_samples);
170       rfBuildVars.assign(daceIterator.all_samples());
171     }
172     rfBuildData.reshape(num_samples, numFns);
173     const IntResponseMap& all_resp = daceIterator.all_responses();
174     IntRespMCIter r_it = all_resp.begin();
175     for (size_t samp=0; samp<num_samples; ++samp, ++r_it)
176       for (size_t fn=0; fn<numFns; ++fn)
177         rfBuildData(samp,fn) = r_it->second.function_value(fn);
178   }
179 }
180 
181 
182 /// Alternative to below function when using RFSuite
rf_suite_identify_field_model()183 void RandomFieldModel::rf_suite_identify_field_model()
184 {
185   actualReducedRank = requestedReducedRank;
186   // Build an RF suite reduced model
187   // dprepro to insert covariance type / parameters / correl lengths, bases, percent variance
188   Cout << "In rf_suite_identify_field_model  " << '\n';
189   //std::system("dprepro covsettings.txt KL_solve.template KL_solve.i");
190   std::system("./run_kl_solve.sh");
191 }
192 
193 
identify_field_model()194 void RandomFieldModel::identify_field_model()
195 {
196   // operations common to both representations
197   rfBasis.set_matrix(rfBuildData);
198   rfBasis.update_svd(true);  // true: center the matrix before factoring
199   //percentVariance = 0.9; // hardcoded: need to remove
200   ReducedBasis::VarianceExplained truncation(percentVariance);
201   actualReducedRank = truncation.get_num_components(rfBasis);
202   Cout << "RandomFieldModel: retaining " << actualReducedRank
203        << " basis functions." << std::endl;
204 
205   switch(expansionForm) {
206   case RF_KARHUNEN_LOEVE: {
207     Cout << "KL Expansion Form " << '\n';
208     break;
209   }
210   case RF_PCA_GP: {
211 
212     Cout << "PCA Expansion Form " << '\n';
213 
214     int num_samples = rfBuildData.numRows();
215 
216     // Get the centered version of the original response matrix
217     const RealMatrix& centered_matrix = rfBasis.get_matrix();
218     // for now get the first factor score
219     const RealMatrix& principal_comp
220       = rfBasis.get_right_singular_vector_transpose();
221 
222     // Compute the factor scores
223     RealMatrix factor_scores(num_samples, numFns);
224     int myerr = factor_scores.multiply(Teuchos::NO_TRANS, Teuchos::TRANS, 1.,
225                                        centered_matrix, principal_comp, 0.);
226 
227     // BMA TODO: verify why this size differs; seems that factor
228     // scores should be n x p and that this could seg fault if
229     // num_samples > numFns...
230     RealMatrix
231       f_scores(Teuchos::Copy, factor_scores, num_samples, num_samples, 0, 0);
232 
233     // build the GP approximations, one per principal component
234     String approx_type("global_kriging"); // Surfpack GP
235     UShortArray approx_order;
236     short data_order = 1;  // assume only function values
237     short output_level = NORMAL_OUTPUT;
238     SharedApproxData sharedData;
239 
240     // BMA TODO: generalize to discrete vars if possible
241     sharedData = SharedApproxData(approx_type, approx_order, subModel.cv(),
242                                   data_order, output_level);
243 
244     // build one GP for each Principal Component
245     gpApproximations.clear();
246     for (int i = 0; i < actualReducedRank; ++i)
247       gpApproximations.push_back(Approximation(sharedData));
248     for (int i = 0; i < actualReducedRank; ++i) {
249       RealVector factor_i = Teuchos::getCol(Teuchos::View,f_scores,i);
250       gpApproximations[i].add_array(rfBuildVars, factor_i);
251       gpApproximations[i].build();
252       const String gp_string = std::to_string(i);
253       const String gp_prefix = "PCA_GP";
254       gpApproximations[i].export_model(StringArray(), gp_string, gp_prefix,
255 				       ALGEBRAIC_FILE);
256     }
257     break;
258   }
259   default:
260     Cerr << "Not implemented";
261     break;
262   }
263 }
264 
265 
266 /** Initialize the recast model to augment the uncertain variables
267     with actualReducedRank additional N(0,1) variables, with no
268     response function mapping (for now).*/
initialize_recast()269 void RandomFieldModel::initialize_recast()
270 {
271   // For now, we assume the map is over all functions, without
272   // distinguishing primary from secondary
273 
274   // ---
275   // Variables mapping: RecastModel maps augmented (uncertainVars +
276   // RFcoeffs) variables to original submodel which accepts only
277   // uncertainVars (plus a field realization)
278   //
279   // BMA TODO: In the case of KL, vars are expanded by RFcoeffs; in
280   // case of PCA/GP, only original uncVars are needed and the vars map
281   // is likely the identity.
282   //
283   // Consider two derived classes or deriving PCA/GP model from
284   // DataFitSurrModel, especially since no Recast is needed for the
285   // PCA/GP case.
286   // ---
287 
288   // We assume the mapping is for all active variables, but only
289   // normal uncertain get modified
290   size_t submodel_vars =
291     subModel.cv()+ subModel.div() + subModel.dsv() + subModel.drv();
292   size_t recast_vars = submodel_vars + actualReducedRank;
293 
294   // BMA TODO: This is wrong!  Assumes normal lead the cv array!
295   UShortMultiArrayConstView sm_cv_types = subModel.continuous_variable_types();
296   size_t num_sm_normal
297     = std::count(sm_cv_types.begin(), sm_cv_types.end(), NORMAL_UNCERTAIN);
298 
299   // TODO: don't assume this for PCA case!
300 
301   // In general, each submodel continuous variable depends on all of
302   // the recast (reduced) variables; others are one-to-one.
303   Sizet2DArray vars_map_indices(submodel_vars);
304   for (size_t i=0; i<submodel_vars; ++i) {
305     vars_map_indices[i].resize(submodel_vars);
306     size_t j = 0;
307     for ( ; j<num_sm_normal; ++j)
308       vars_map_indices[i][j] = j;
309     // skip the N(0,1) coeffs, if they exist
310     for ( ; j<num_sm_normal; ++j)
311       vars_map_indices[i][j] = actualReducedRank + j;
312   }
313   // Variables map is linear
314   bool nonlinear_vars_mapping = false;
315 
316   SizetArray vars_comps_total = variables_resize();
317   BitArray all_relax_di, all_relax_dr; // default: empty; no discrete relaxation
318 
319   // Primary and secondary mapping are one-to-one (NULL callbacks)
320   // TODO: can we get RecastModel to tolerate empty indices when no
321   // map is present?
322   size_t num_primary = subModel.num_primary_fns(),
323     num_secondary    = subModel.num_secondary_fns(),
324     num_recast_fns   = num_primary + num_secondary,
325     recast_secondary_offset = subModel.num_nonlinear_ineq_constraints();
326 
327   Sizet2DArray primary_resp_map_indices(num_primary);
328   for (size_t i=0; i<num_primary; i++) {
329     primary_resp_map_indices[i].resize(1);
330     primary_resp_map_indices[i][0] = i;
331   }
332 
333   Sizet2DArray secondary_resp_map_indices(num_secondary);
334   for (size_t i=0; i<num_secondary; i++) {
335     secondary_resp_map_indices[i].resize(1);
336     secondary_resp_map_indices[i][0] = num_primary + i;
337   }
338 
339   BoolDequeArray nonlinear_resp_mapping(numFns, BoolDeque(numFns, false));
340 
341   // Initial response order for the newly built subspace model same as
342   // the subModel (does not augment with gradient request)
343   const Response& curr_resp = subModel.current_response();
344   short recast_resp_order = 1; // recast resp order to be same as original resp
345   if (!curr_resp.function_gradients().empty()) recast_resp_order |= 2;
346   if (!curr_resp.function_hessians().empty())  recast_resp_order |= 4;
347 
348   RecastModel::
349     init_sizes(vars_comps_total, all_relax_di, all_relax_dr, num_primary,
350 	       num_secondary, recast_secondary_offset, recast_resp_order);
351 
352   RecastModel::
353     init_maps(vars_map_indices, nonlinear_vars_mapping, vars_mapping,
354 	      set_mapping, primary_resp_map_indices, secondary_resp_map_indices,
355 	      nonlinear_resp_mapping, NULL, NULL);
356 }
357 
358 
359 /// Create a variables components totals array with the reduced space
360 /// size for continuous variables
361 /// TODO: augment normal uncVars for KL case
variables_resize()362 SizetArray RandomFieldModel::variables_resize()
363 {
364   const SharedVariablesData& svd = subModel.current_variables().shared_data();
365   SizetArray vc_totals = svd.components_totals();
366 
367   // the map size only changes for KL to augment the coeffs by
368   // actualReducedRank N(0,1) variables
369   if (expansionForm == RF_KARHUNEN_LOEVE) {
370      // TODO: validate active view on subModel in case there are now CAUV
371      vc_totals[TOTAL_CAUV] += actualReducedRank;
372   }
373   return vc_totals;
374 }
375 
376 
377 /** Initialzie the aleatory dist params for the KL coeffs */
initialize_rf_coeffs()378 void RandomFieldModel::initialize_rf_coeffs()
379 {
380   // BMA TODO: more gracefully insert the variables into their place
381 
382   // the map size only changes for KL to augment the coeffs by
383   // actualReducedRank N(0,1) variables
384   if (expansionForm == RF_KARHUNEN_LOEVE) {
385 
386     // get submodel normal parameters (could get from current object as well)
387     const Pecos::MultivariateDistribution& sm_dist
388       = subModel.multivariate_distribution();
389     std::shared_ptr<Pecos::MarginalsCorrDistribution> sm_mvd_rep =
390       std::static_pointer_cast<Pecos::MarginalsCorrDistribution>
391       (sm_dist.multivar_dist_rep());
392     RealVector normal_means, normal_stdev, normal_lb, normal_ub;
393     sm_mvd_rep->pull_parameters(Pecos::NORMAL, Pecos::N_MEAN,    normal_means);
394     sm_mvd_rep->pull_parameters(Pecos::NORMAL, Pecos::N_STD_DEV, normal_stdev);
395     sm_mvd_rep->pull_parameters(Pecos::NORMAL, Pecos::N_LWR_BND, normal_lb);
396     sm_mvd_rep->pull_parameters(Pecos::NORMAL, Pecos::N_UPR_BND, normal_ub);
397 
398     // append normal variables
399     int num_sm_normal = normal_means.length();
400     normal_means.resize(num_sm_normal + actualReducedRank);
401     normal_stdev.resize(num_sm_normal + actualReducedRank);
402     normal_lb.resize(num_sm_normal + actualReducedRank);
403     normal_ub.resize(num_sm_normal + actualReducedRank);
404     // BMA TODO: update label management to not assume normal are leading vars
405     StringMultiArrayConstView sm_cv_labels =
406       subModel.continuous_variable_labels();
407     for (int i=0 ; i<num_sm_normal; ++i)
408       currentVariables.continuous_variable_label(sm_cv_labels[i], i);
409     for (int i=0; i<actualReducedRank; ++i) {
410       normal_means[num_sm_normal + i] = 0.0;
411       normal_stdev[num_sm_normal + i] = 1.0;
412       normal_lb[num_sm_normal + i] = -std::numeric_limits<Real>::infinity();
413       normal_ub[num_sm_normal + i] =  std::numeric_limits<Real>::infinity();
414       String xi_label = "xi_" + std::to_string(i+1);
415       currentVariables.
416         continuous_variable_label(xi_label, num_sm_normal + i);
417     }
418     for (int i=num_sm_normal; i<sm_cv_labels.size(); ++i)
419       currentVariables.
420         continuous_variable_label(sm_cv_labels[i], actualReducedRank + i);
421 
422     // update mvDist for the RandomFieldModel
423     std::shared_ptr<Pecos::MarginalsCorrDistribution> mvd_rep =
424       std::static_pointer_cast<Pecos::MarginalsCorrDistribution>
425       (mvDist.multivar_dist_rep());
426     mvd_rep->push_parameters(Pecos::NORMAL, Pecos::N_MEAN,    normal_means);
427     mvd_rep->push_parameters(Pecos::NORMAL, Pecos::N_STD_DEV, normal_stdev);
428     mvd_rep->push_parameters(Pecos::NORMAL, Pecos::N_LWR_BND, normal_lb);
429     mvd_rep->push_parameters(Pecos::NORMAL, Pecos::N_UPR_BND, normal_ub);
430   }
431 }
432 
433 /// map the active continuous recast variables to the active
434 /// submodel variables
vars_mapping(const Variables & recast_augmented_vars,Variables & sub_model_vars)435 void RandomFieldModel::vars_mapping(const Variables& recast_augmented_vars,
436                                     Variables& sub_model_vars)
437 {
438   if (rfmInstance->expansionForm == RF_KARHUNEN_LOEVE) {
439     // send the submodel all but the N(0,1) vars
440 
441     // BMA TODO: generalize this for other views; for now, assume cv()
442     // starts with normal uncertain
443     size_t num_sm_cv = rfmInstance->subModel.cv();
444     UShortMultiArrayConstView sm_cv_types
445       = rfmInstance->subModel.continuous_variable_types();
446     size_t num_sm_normal
447       = std::count(sm_cv_types.begin(), sm_cv_types.end(), NORMAL_UNCERTAIN);
448 
449     const RealVector& augmented_cvars =
450       recast_augmented_vars.continuous_variables();
451 
452     size_t i = 0;
453     RealVector sm_cvars(num_sm_cv);
454     for ( ; i<num_sm_normal; ++i)
455       sm_cvars[i] = augmented_cvars[i];
456     // skip the N(0,1) coeffs, if they exist
457     for ( ; i<num_sm_cv; ++i)
458       sm_cvars[i] = augmented_cvars[rfmInstance->actualReducedRank + i];
459 
460     // propagate variables to subModel
461     sub_model_vars.continuous_variables(sm_cvars);
462     sub_model_vars.discrete_int_variables(recast_augmented_vars.
463                                           discrete_int_variables());
464     sub_model_vars.discrete_string_variables(recast_augmented_vars.
465                                              discrete_string_variables());
466     sub_model_vars.discrete_real_variables(recast_augmented_vars.
467                                            discrete_real_variables());
468   }
469   else
470     sub_model_vars.active_variables(recast_augmented_vars);
471 }
472 
473 
derived_evaluate(const ActiveSet & set)474 void RandomFieldModel::derived_evaluate(const ActiveSet& set)
475 {
476   fieldRealizationId++;
477 
478   if (expansionForm == RF_KARHUNEN_LOEVE)
479     generate_kl_realization();
480   else if (expansionForm == RF_PCA_GP)
481     generate_pca_gp_realization();
482 
483   // May need hook to communicate the appropraiate mesh file to the simulation
484 
485   RecastModel::derived_evaluate(set);
486 
487 }
488 
489 
derived_evaluate_nowait(const ActiveSet & set)490 void RandomFieldModel::derived_evaluate_nowait(const ActiveSet& set)
491 {
492   fieldRealizationId++;
493 
494   if (expansionForm == RF_KARHUNEN_LOEVE)
495     generate_kl_realization();
496   else if (expansionForm == RF_PCA_GP)
497     generate_pca_gp_realization();
498 
499   RecastModel::derived_evaluate_nowait(set);
500 }
501 
502 
generate_kl_realization()503 void RandomFieldModel::generate_kl_realization()
504 {
505   // ReducedBasis gives the singular values of the centered data
506   // matrix; the covariance is scaled by n-1:
507   int cov_dof = std::sqrt( (double)rfBasis.get_matrix().numRows() - 1 );
508   const RealVector& data_singular_values = rfBasis.get_singular_values();
509   RealMatrix rf_ev_trans = rfBasis.get_right_singular_vector_transpose();
510 
511   // extract N(0,1) KL coefficients to generate a field realization
512   //
513   // BMA TODO: properly extract the N(0,1) vars from their place in
514   // the overall vector when not in aleatory view (what's in the total
515   // vector depends on the view...)
516   UShortMultiArrayConstView sm_cv_types = subModel.continuous_variable_types();
517   size_t num_sm_normal
518     = std::count(sm_cv_types.begin(), sm_cv_types.end(), NORMAL_UNCERTAIN);
519   const RealVector& augmented_cvars = currentVariables.continuous_variables();
520   RealVector kl_coeffs(Teuchos::View, augmented_cvars.values() + num_sm_normal,
521                        actualReducedRank);
522 
523   if (outputLevel >= DEBUG_OUTPUT) {
524     Cout << "Augmented continuous variables:\n" << augmented_cvars << std::endl;
525     Cout << "KL random coeffs:\n" << kl_coeffs << std::endl;
526   }
527 
528   // Run KL realize with RF Suite
529   // dprepro to configure kl_realize.i with kl_coeffs, mesh number
530   //std::system("dprepro randomcoeffs.txt KL_realize.template KL_realize.i");
531   //std::system("launch -n 1 encore -i KL_realize.i");
532 
533   if (covarianceForm != NOCOVAR)
534     std::system("./run_kl_realize.sh");
535 
536   // post-condition: mesh file gets written containing the field realization
537   // probably want to tag it with evalID...
538   // bfs::copy(mesh.exo mesh.realize1.exo)
539   // need to obtain the realization ID for this Model/Interface
540 
541   // For now use fieldRealizationId
542 
543   // May want to create a new derived Interface from
544   // ForkApplicInterface called ExternalFieldManagerInterface
545   // in order to better manage hierarchical tagging
546 
547   // augment the mean prediction with the covariance contributions
548   RealVector kl_prediction = rfBasis.get_column_means();
549   for (int i=0; i<actualReducedRank; ++i) {
550     // BMA TODO: replace with matrix-vector ops (perhaps in ReducedBasis)
551     Real mult = data_singular_values[i]/cov_dof * kl_coeffs[i];
552     for (int k=0; k<numFns; ++k)
553       kl_prediction[k] += mult*rf_ev_trans(i,k);
554   }
555 
556   write_field(kl_prediction);
557 }
558 
559 
generate_pca_gp_realization()560 void RandomFieldModel::generate_pca_gp_realization()
561 {
562   // augment the mean prediction with the GP contributions
563   RealVector pca_prediction = rfBasis.get_column_means();
564   // reduced basis comprised of the rows of V'
565   const RealMatrix& principal_comp
566     = rfBasis.get_right_singular_vector_transpose();
567 
568   for (int i=0; i<actualReducedRank; ++i) {
569     const RealVector& new_sample = currentVariables.continuous_variables();
570     Real pca_coeff = gpApproximations[i].value(new_sample);
571     if (outputLevel == DEBUG_OUTPUT)
572       Cout << "DEBUG: pca_coeff = " << pca_coeff << '\n';
573     for (int k=0; k<numFns; ++k)
574       pca_prediction[k] += pca_coeff*principal_comp(i,k);
575   }
576 
577   write_field(pca_prediction);
578 }
579 
580 
write_field(const RealVector & field_prediction)581 void RandomFieldModel::write_field(const RealVector& field_prediction)
582 {
583   // TODO: write to file per eval, preferrably in work_directory
584   // BMA TODO: something smarter than modelEvalCntr / eval ID
585   if (outputLevel >= VERBOSE_OUTPUT) {
586     String pred_count(std::to_string(evaluation_id() + 1));
587     std::ofstream myfile;
588     myfile.open(("field_prediction." + pred_count + ".txt").c_str());
589     Cout << "Field prediction " << pred_count << "\n";
590     Cout << field_prediction << std::endl;
591     for (int i=0; i<field_prediction.length(); ++i)
592       myfile << field_prediction[i] << " ";
593     myfile << std::endl;
594   }
595 }
596 
597 
598 /// map the inbound ActiveSet to the sub-model (map derivative variables)
set_mapping(const Variables & recast_vars,const ActiveSet & recast_set,ActiveSet & sub_model_set)599 void RandomFieldModel::set_mapping(const Variables& recast_vars,
600 				   const ActiveSet& recast_set,
601 				   ActiveSet& sub_model_set)
602 { }
603 
604 }
605