[P]arallel [Hi]gh-order [Li]brary for [P]DEs  Latest
Parallel High-Order Library for PDEs through hp-adaptive Discontinuous Galerkin methods
adaptive_sampling_base.cpp
1 #include "adaptive_sampling_base.h"
2 #include <iostream>
3 #include <filesystem>
4 #include "functional/functional.h"
5 #include <deal.II/lac/trilinos_sparse_matrix.h>
6 #include <deal.II/lac/vector_operation.h>
7 #include "reduced_order_solution.h"
8 #include "flow_solver/flow_solver.h"
9 #include "flow_solver/flow_solver_factory.h"
10 #include "testing/rom_import_helper_functions.h"
11 #include <cmath>
12 #include "rbf_interpolation.h"
13 #include "ROL_Algorithm.hpp"
14 #include "ROL_LineSearchStep.hpp"
15 #include "ROL_StatusTest.hpp"
16 #include "ROL_Stream.hpp"
17 #include "ROL_Bounds.hpp"
18 #include "halton.h"
19 #include "min_max_scaler.h"
20 
21 namespace PHiLiP {
22 
23 template<int dim, int nstate>
25  const dealii::ParameterHandler &parameter_handler_input)
26  : all_parameters(parameters_input)
27  , parameter_handler(parameter_handler_input)
28  , mpi_communicator(MPI_COMM_WORLD)
29  , mpi_rank(dealii::Utilities::MPI::this_mpi_process(MPI_COMM_WORLD))
30  , pcout(std::cout, mpi_rank==0)
31 {
33  std::unique_ptr<FlowSolver::FlowSolver<dim,nstate>> flow_solver = FlowSolver::FlowSolverFactory<dim,nstate>::select_flow_case(all_parameters, parameter_handler);
34  const bool compute_dRdW = true;
35  flow_solver->dg->assemble_residual(compute_dRdW);
36  std::shared_ptr<dealii::TrilinosWrappers::SparseMatrix> system_matrix = std::make_shared<dealii::TrilinosWrappers::SparseMatrix>();
37  system_matrix->copy_from(flow_solver->dg->system_matrix);
38  current_pod = std::make_shared<ProperOrthogonalDecomposition::OnlinePOD<dim>>(system_matrix);
39  nearest_neighbors = std::make_shared<ProperOrthogonalDecomposition::NearestNeighbors>();
40 }
41 
42 template <int dim, int nstate>
44  std::unique_ptr<dealii::TableHandler> snapshot_table = std::make_unique<dealii::TableHandler>();
45 
46  std::ofstream solution_out_file("solution_snapshots_iteration_" + iteration + ".txt");
47  unsigned int precision = 16;
48  current_pod->dealiiSnapshotMatrix.print_formatted(solution_out_file, precision);
49  solution_out_file.close();
50 
51  for(auto parameters : snapshot_parameters.rowwise()){
52  for(int i = 0 ; i < snapshot_parameters.cols() ; i++){
53  snapshot_table->add_value(all_parameters->reduced_order_param.parameter_names[i], parameters(i));
54  snapshot_table->set_precision(all_parameters->reduced_order_param.parameter_names[i], 16);
55  }
56  }
57 
58  std::ofstream snapshot_table_file("snapshot_table_iteration_" + iteration + ".txt");
59  snapshot_table->write_text(snapshot_table_file, dealii::TableHandler::TextOutputFormat::org_mode_table);
60  snapshot_table_file.close();
61 
62  std::unique_ptr<dealii::TableHandler> rom_table = std::make_unique<dealii::TableHandler>();
63 
64  for(auto it = rom_locations.begin(); it != rom_locations.end(); ++it){
65  for(int i = 0 ; i < snapshot_parameters.cols() ; i++){
66  rom_table->add_value(all_parameters->reduced_order_param.parameter_names[i], it->get()->parameter(i));
67  rom_table->set_precision(all_parameters->reduced_order_param.parameter_names[i], 16);
68  }
69  rom_table->add_value("ROM_errors", it->get()->total_error);
70  rom_table->set_precision("ROM_errors", 16);
71  }
72 
73  std::ofstream rom_table_file("rom_table_iteration_" + iteration + ".txt");
74  rom_table->write_text(rom_table_file, dealii::TableHandler::TextOutputFormat::org_mode_table);
75  rom_table_file.close();
76 }
77 
78 
79 template <int dim, int nstate>
81  RowVectorXd params(1);
82 
83  using FlowCaseEnum = Parameters::FlowSolverParam::FlowCaseType;
84  const FlowCaseEnum flow_type = this->all_parameters->flow_solver_param.flow_case_type;
85 
86  if (flow_type == FlowCaseEnum::burgers_rewienski_snapshot){
87  if(this->all_parameters->reduced_order_param.parameter_names.size() == 1){
88  if(this->all_parameters->reduced_order_param.parameter_names[0] == "rewienski_a"){
89  params(0) = this->all_parameters->burgers_param.rewienski_a;
90  }
91  else if(this->all_parameters->reduced_order_param.parameter_names[0] == "rewienski_b"){
92  params(0) = this->all_parameters->burgers_param.rewienski_b;
93  }
94  }
95  else{
96  params.resize(2);
97  params(0) = this->all_parameters->burgers_param.rewienski_a;
98  params(1) = this->all_parameters->burgers_param.rewienski_b;
99  }
100  }
101  else if (flow_type == FlowCaseEnum::naca0012){
102  if(this->all_parameters->reduced_order_param.parameter_names.size() == 1){
103  if(this->all_parameters->reduced_order_param.parameter_names[0] == "mach"){
104  params(0) = this->all_parameters->euler_param.mach_inf;
105  }
106  else if(this->all_parameters->reduced_order_param.parameter_names[0] == "alpha"){
107  params(0) = this->all_parameters->euler_param.angle_of_attack;
108  }
109  }
110  else{
111  params.resize(2);
112  params(0) = this->all_parameters->euler_param.mach_inf;
113  params(1) = this->all_parameters->euler_param.angle_of_attack; //radians!
114  }
115  }
116  else if (flow_type == FlowCaseEnum::gaussian_bump){
117  if(this->all_parameters->reduced_order_param.parameter_names.size() == 1){
118  if(this->all_parameters->reduced_order_param.parameter_names[0] == "mach"){
119  params(0) = this->all_parameters->euler_param.mach_inf;
120  }
121  }
122  }
123  else{
124  this->pcout << "Invalid flow case. You probably forgot to specify a flow case in the prm file." << std::endl;
125  std::abort();
126  }
127  return params;
128 }
129 
130 template <int dim, int nstate>
132  this->pcout << "Updating RBF interpolation..." << std::endl;
133 
134  int n_rows = snapshot_parameters.rows() + rom_locations.size();
135  MatrixXd parameters(n_rows, snapshot_parameters.cols());
136  VectorXd errors(n_rows);
137 
138  int i;
139  // Loop through FOM snapshot locations and add zero error to errors vector
140  for(i = 0 ; i < snapshot_parameters.rows() ; i++){
141  errors(i) = 0;
142  parameters.row(i) = snapshot_parameters.row(i);
143  }
144  this->pcout << i << std::endl;
145  // Loop through ROM points and add total error to errors vector (both FOM snaps and ROM points are used to build RBF)
146  for(auto it = rom_locations.begin(); it != rom_locations.end(); ++it){
147  parameters.row(i) = it->get()->parameter.array();
148  errors(i) = it->get()->total_error;
149  i++;
150  }
151 
152  // Must scale both axes between [0,1] for the 2d rbf interpolation to work optimally
154  MatrixXd parameters_scaled = scaler.fit_transform(parameters);
155 
156  // Construct radial basis function
157  std::string kernel = "thin_plate_spline";
159 
160  // Set parameters.
161  ROL::ParameterList parlist;
162  parlist.sublist("Step").sublist("Line Search").sublist("Line-Search Method").set("Type","Backtracking");
163  parlist.sublist("Step").sublist("Line Search").sublist("Descent Method").set("Type","Quasi-Newton Method");
164  parlist.sublist("Step").sublist("Line Search").sublist("Secant").set("Type","Limited-Memory BFGS");
165  parlist.sublist("Status Test").set("Gradient Tolerance",1.e-10);
166  parlist.sublist("Status Test").set("Step Tolerance",1.e-14);
167  parlist.sublist("Status Test").set("Iteration Limit",100);
168 
169  // Find max error and parameters by minimizing function starting at each ROM location
170  RowVectorXd max_error_params(parameters.cols());
171  max_error = 0;
172 
173  for(auto it = rom_locations.begin(); it != rom_locations.end(); ++it){
174 
175  Eigen::RowVectorXd rom_unscaled = it->get()->parameter;
176  Eigen::RowVectorXd rom_scaled = scaler.transform(rom_unscaled);
177 
178  //start bounds
179  int dimension = parameters.cols();
180  ROL::Ptr<std::vector<double>> l_ptr = ROL::makePtr<std::vector<double>>(dimension,0.0);
181  ROL::Ptr<std::vector<double>> u_ptr = ROL::makePtr<std::vector<double>>(dimension,1.0);
182  ROL::Ptr<ROL::Vector<double>> lo = ROL::makePtr<ROL::StdVector<double>>(l_ptr);
183  ROL::Ptr<ROL::Vector<double>> up = ROL::makePtr<ROL::StdVector<double>>(u_ptr);
184  ROL::Bounds<double> bcon(lo,up);
185  //end bounds
186 
187  ROL::Ptr<ROL::Step<double>> step = ROL::makePtr<ROL::LineSearchStep<double>>(parlist);
188  ROL::Ptr<ROL::StatusTest<double>> status = ROL::makePtr<ROL::StatusTest<double>>(parlist);
189  ROL::Algorithm<double> algo(step,status,false);
190 
191  // Iteration Vector
192  ROL::Ptr<std::vector<double>> x_ptr = ROL::makePtr<std::vector<double>>(dimension, 0.0);
193 
194  // Set Initial Guess
195  for(int j = 0 ; j < dimension ; j++){
196  (*x_ptr)[j] = rom_scaled(j);
197  }
198 
199  this->pcout << "Unscaled parameter: " << rom_unscaled << std::endl;
200  this->pcout << "Scaled parameter: ";
201  for(int j = 0 ; j < dimension ; j++){
202  this->pcout << (*x_ptr)[j] << " ";
203  }
204  this->pcout << std::endl;
205 
206  ROL::StdVector<double> x(x_ptr);
207 
208  // Run Algorithm
209  algo.run(x, rbf, bcon,false);
210 
211  ROL::Ptr<std::vector<double>> x_min = x.getVector();
212 
213  for(int j = 0 ; j < dimension ; j++){
214  rom_scaled(j) = (*x_min)[j];
215  }
216 
217  RowVectorXd rom_unscaled_optim = scaler.inverse_transform(rom_scaled);
218 
219  this->pcout << "Parameters of optimization convergence: " << rom_unscaled_optim << std::endl;
220 
221  double error = std::abs(rbf.evaluate(rom_scaled));
222  this->pcout << "RBF error at optimization convergence: " << error << std::endl;
223  if(error > max_error){
224  this->pcout << "RBF error is greater than current max error. Updating max error." << std::endl;
225  max_error = error;
226  max_error_params = rom_unscaled_optim;
227  this->pcout << "RBF Max error: " << max_error << std::endl;
228  }
229  }
230 
231  // Check if max_error_params is a ROM point
232  for(auto it = rom_locations.begin(); it != rom_locations.end(); ++it){
233  if(max_error_params.isApprox(it->get()->parameter)){
234  this->pcout << "Max error location is approximately the same as a ROM location. Removing ROM location." << std::endl;
235  rom_locations.erase(it);
236  break;
237  }
238  }
239 
240  return max_error_params;
241 }
242 
243 template <int dim, int nstate>
245  for(auto snap_param : snapshot_parameters.rowwise()){
246  this->pcout << "Sampling initial snapshot at " << snap_param << std::endl;
247  dealii::LinearAlgebra::distributed::Vector<double> fom_solution = solveSnapshotFOM(snap_param);
248  nearest_neighbors->update_snapshots(snapshot_parameters, fom_solution);
249  current_pod->addSnapshot(fom_solution);
250  this->fom_locations.emplace_back(fom_solution);
251  }
252 }
253 
254 template <int dim, int nstate>
255 dealii::LinearAlgebra::distributed::Vector<double> AdaptiveSamplingBase<dim, nstate>::solveSnapshotFOM(const RowVectorXd& parameter) const{
256  this->pcout << "Solving FOM at " << parameter << std::endl;
257  Parameters::AllParameters params = reinit_params(parameter);
258 
259  std::unique_ptr<FlowSolver::FlowSolver<dim,nstate>> flow_solver = FlowSolver::FlowSolverFactory<dim,nstate>::select_flow_case(&params, parameter_handler);
260 
261  // Solve implicit solution
262  auto ode_solver_type = Parameters::ODESolverParam::ODESolverEnum::implicit_solver;
263  flow_solver->ode_solver = PHiLiP::ODE::ODESolverFactory<dim, double>::create_ODESolver_manual(ode_solver_type, flow_solver->dg);
264  flow_solver->ode_solver->allocate_ode_system();
265  flow_solver->run();
266 
267  this->pcout << "Done solving FOM." << std::endl;
268  return flow_solver->dg->solution;
269 }
270 
271 template <int dim, int nstate>
273  // Copy all parameters
275 
276  using FlowCaseEnum = Parameters::FlowSolverParam::FlowCaseType;
277  const FlowCaseEnum flow_type = this->all_parameters->flow_solver_param.flow_case_type;
278 
279  if (flow_type == FlowCaseEnum::burgers_rewienski_snapshot){
281  if(all_parameters->reduced_order_param.parameter_names[0] == "rewienski_a"){
282  parameters.burgers_param.rewienski_a = parameter(0);
283  }
284  else if(all_parameters->reduced_order_param.parameter_names[0] == "rewienski_b"){
285  parameters.burgers_param.rewienski_b = parameter(0);
286  }
287  }
288  else{
289  parameters.burgers_param.rewienski_a = parameter(0);
290  parameters.burgers_param.rewienski_b = parameter(1);
291  }
292  }
293  else if (flow_type == FlowCaseEnum::naca0012){
296  parameters.euler_param.mach_inf = parameter(0);
297  }
298  else if(all_parameters->reduced_order_param.parameter_names[0] == "alpha"){
299  parameters.euler_param.angle_of_attack = parameter(0); //radians!
300  }
301  }
302  else{
303  parameters.euler_param.mach_inf = parameter(0);
304  parameters.euler_param.angle_of_attack = parameter(1); //radians!
305  }
306  }
307  else if (flow_type == FlowCaseEnum::gaussian_bump){
310  parameters.euler_param.mach_inf = parameter(0);
311  }
312  }
313  }
314  else{
315  this->pcout << "Invalid flow case. You probably forgot to specify a flow case in the prm file." << std::endl;
316  std::abort();
317  }
318  return parameters;
319 }
320 
321 template <int dim, int nstate>
323 {
324  const double pi = atan(1.0) * 4.0;
325 
327 
329  RowVectorXd parameter1_range;
330  parameter1_range.resize(2);
333  parameter1_range *= pi/180; // convert to radians
334  }
335 
336  // Place parameters at 2 ends and center
337  snapshot_parameters.resize(3,1);
338  snapshot_parameters << parameter1_range[0],
339  parameter1_range[1],
340  (parameter1_range[0]+parameter1_range[1])/2;
341 
342  snapshot_parameters.conservativeResize(snapshot_parameters.rows() + n_halton, snapshot_parameters.cols());
343 
344  double *seq = nullptr;
345  for (int i = 0; i < n_halton; i++)
346  {
347  seq = ProperOrthogonalDecomposition::halton(i+2, 1); //ignore the first two Halton point as they are the left end and center
348  snapshot_parameters(i+3) = seq[0]*(parameter1_range[1] - parameter1_range[0])+parameter1_range[0];
349  }
350  delete [] seq;
351 
352  this->pcout << snapshot_parameters << std::endl;
353  }
354  else if(all_parameters->reduced_order_param.parameter_names.size() == 2){
355  RowVectorXd parameter1_range;
356  parameter1_range.resize(2);
358 
359  RowVectorXd parameter2_range;
360  parameter2_range.resize(2);
363  parameter2_range *= pi/180; // convert to radians
364  }
365 
366  // Place 9 parameters in a grid
367  snapshot_parameters.resize(9,2);
368  snapshot_parameters << parameter1_range[0], parameter2_range[0],
369  parameter1_range[0], parameter2_range[1],
370  parameter1_range[1], parameter2_range[1],
371  parameter1_range[1], parameter2_range[0],
372  0.5*(parameter1_range[1] - parameter1_range[0])+parameter1_range[0], parameter2_range[0],
373  0.5*(parameter1_range[1] - parameter1_range[0])+parameter1_range[0], parameter2_range[1],
374  parameter1_range[0], 0.5*(parameter2_range[1] - parameter2_range[0])+parameter2_range[0],
375  parameter1_range[1], 0.5*(parameter2_range[1] - parameter2_range[0])+parameter2_range[0],
376  0.5*(parameter1_range[1] - parameter1_range[0])+parameter1_range[0], 0.5*(parameter2_range[1] - parameter2_range[0])+parameter2_range[0];
377 
378  snapshot_parameters.conservativeResize(snapshot_parameters.rows() + n_halton, snapshot_parameters.cols());
379 
380  double *seq = nullptr;
381  for (int i = 0; i < n_halton; i++)
382  {
383  seq = ProperOrthogonalDecomposition::halton(i+1, 2); //ignore the first Halton point as it is one of the corners
384  for (int j = 0; j < 2; j++)
385  {
386  if(j == 0){
387  snapshot_parameters(i+9, j) = seq[j]*(parameter1_range[1] - parameter1_range[0])+parameter1_range[0];
388  }
389  else if(j == 1){
390  snapshot_parameters(i+9, j) = seq[j]*(parameter2_range[1] - parameter2_range[0])+parameter2_range[0];
391  }
392  }
393  }
394  delete [] seq;
395 
397  this->pcout << path << std::endl;
398  if(!path.empty()){
399  this->pcout << "LHS Points " << std::endl;
401  Tests::getSnapshotParamsFromFile(snapshot_parameters, path);
402  }
403 
404  this->pcout << snapshot_parameters << std::endl;
405  }
406 }
407 
408 #if PHILIP_DIM==1
410 #endif
411 
412 #if PHILIP_DIM!=1
414 #endif
415 
416 }
static std::shared_ptr< ODESolverBase< dim, real, MeshType > > create_ODESolver_manual(Parameters::ODESolverParam::ODESolverEnum ode_solver_type, std::shared_ptr< DGBase< dim, real, MeshType > > dg_input)
Creates either implicit or explicit ODE solver based on manual input (no POD basis given) ...
FlowCaseType
Selects the flow case to be simulated.
FlowCaseType flow_case_type
Selected FlowCaseType from the input file.
const dealii::ParameterHandler & parameter_handler
Parameter handler for storing the .prm file being ran.
FlowSolverParam flow_solver_param
Contains the parameters for simulation cases (flow solver test)
std::vector< std::string > parameter_names
Names of parameters.
double evaluate(const RowVectorXd &evaluate_coordinate) const
Evaluate RBF.
dealii::ConditionalOStream pcout
ConditionalOStream.
RowVectorXd readROMFunctionalPoint() const
Find point to solve for functional from param file.
std::shared_ptr< ProperOrthogonalDecomposition::NearestNeighbors > nearest_neighbors
Nearest neighbors of snapshots.
double mach_inf
Mach number at infinity.
int num_halton
Number of Halton sequence points to add to initial snapshot set.
std::vector< double > parameter_min_values
Minimum value of parameters.
dealii::LinearAlgebra::distributed::Vector< double > solveSnapshotFOM(const RowVectorXd &parameter) const
Solve full-order snapshot.
Files for the baseline physics.
Definition: ADTypes.hpp:10
ReducedOrderModelParam reduced_order_param
Contains parameters for the Reduced-Order model.
EulerParam euler_param
Contains parameters for the Euler equations non-dimensionalization.
void configureInitialParameterSpace() const
Set up parameter space depending on test case.
Main parameter class that contains the various other sub-parameter classes.
MatrixXd fit_transform(const MatrixXd &snapshot_parameters)
Fit and transform data.
static std::unique_ptr< FlowSolver< dim, nstate > > select_flow_case(const Parameters::AllParameters *const parameters_input, const dealii::ParameterHandler &parameter_handler_input)
Factory to return the correct flow solver given input file.
Parameters::AllParameters reinit_params(const RowVectorXd &parameter) const
Reinitialize parameters.
std::shared_ptr< ProperOrthogonalDecomposition::OnlinePOD< dim > > current_pod
Most up to date POD basis.
std::string file_path_for_snapshot_locations
Path to search for file with pre-determined snapshot locations used to build POD (actual FOM snapshot...
virtual void outputIterationData(std::string iteration) const
Output for each iteration.
void placeInitialSnapshots() const
Placement of initial snapshots.
double angle_of_attack
Input file provides in degrees, but the value stored here is in radians.
std::vector< double > parameter_max_values
Maximum value of parameters.
AdaptiveSamplingBase(const PHiLiP::Parameters::AllParameters *const parameters_input, const dealii::ParameterHandler &parameter_handler_input)
Default constructor that will set the constants.
MatrixXd inverse_transform(const MatrixXd &snapshot_parameters)
Unscale data.
MatrixXd transform(const MatrixXd &snapshot_parameters)
Transform data to previously fitted dataset.
std::vector< dealii::LinearAlgebra::distributed::Vector< double > > fom_locations
Vector of parameter-ROMTestLocation pairs.
MatrixXd snapshot_parameters
Matrix of snapshot parameters.
const Parameters::AllParameters *const all_parameters
Pointer to all parameters.
BurgersParam burgers_param
Contains parameters for Burgers equation.
std::vector< std::unique_ptr< ProperOrthogonalDecomposition::ROMTestLocation< dim, nstate > > > rom_locations
Vector of parameter-ROMTestLocation pairs.
virtual RowVectorXd getMaxErrorROM() const
Compute RBF and find max error.