[P]arallel [Hi]gh-order [Li]brary for [P]DEs  Latest
Parallel High-Order Library for PDEs through hp-adaptive Discontinuous Galerkin methods
pod_adaptive_sampling.cpp
1 #include "pod_adaptive_sampling.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 <cmath>
11 #include "rbf_interpolation.h"
12 #include "ROL_Algorithm.hpp"
13 #include "ROL_LineSearchStep.hpp"
14 #include "ROL_StatusTest.hpp"
15 #include "ROL_Stream.hpp"
16 #include "ROL_Bounds.hpp"
17 #include "halton.h"
18 #include "min_max_scaler.h"
19 #include <deal.II/base/timer.h>
20 
21 namespace PHiLiP {
22 
23 template<int dim, int nspecies, int nstate>
25  const dealii::ParameterHandler &parameter_handler_input)
26  : AdaptiveSamplingBase<dim, nspecies, nstate>(parameters_input, parameter_handler_input)
27 {}
28 
29 template <int dim, int nspecies, int nstate>
31 {
32  this->pcout << "Starting adaptive sampling process" << std::endl;
33  auto stream = this->pcout;
34  dealii::TimerOutput timer(stream,dealii::TimerOutput::summary,dealii::TimerOutput::wall_times);
35  int iteration = 0;
36  timer.enter_subsection ("Iteration " + std::to_string(iteration));
37  this->placeInitialSnapshots();
38  this->current_pod->computeBasis();
39 
40  MatrixXd rom_points = this->nearest_neighbors->kPairwiseNearestNeighborsMidpoint();
41  this->pcout << rom_points << std::endl;
42 
43  placeROMLocations(rom_points);
44 
45  RowVectorXd max_error_params = this->getMaxErrorROM();
46 
47  RowVectorXd functional_ROM = this->readROMFunctionalPoint();
48 
49  this->pcout << "Solving FOM at " << functional_ROM << std::endl;
50 
51  Parameters::AllParameters params = this->reinit_params(functional_ROM);
52  std::unique_ptr<FlowSolver::FlowSolver<dim,nspecies,nstate>> flow_solver_FOM = FlowSolver::FlowSolverFactory<dim,nspecies,nstate>::select_flow_case(&params, this->parameter_handler);
53 
54  // Solve implicit solution
55  auto ode_solver_type = Parameters::ODESolverParam::ODESolverEnum::implicit_solver;
56  flow_solver_FOM->ode_solver = PHiLiP::ODE::ODESolverFactory<dim, nspecies, double>::create_ODESolver_manual(ode_solver_type, flow_solver_FOM->dg);
57  flow_solver_FOM->ode_solver->allocate_ode_system();
58  flow_solver_FOM->run();
59 
60  // Create functional
61  std::shared_ptr<Functional<dim,nspecies,nstate,double>> functional_FOM = FunctionalFactory<dim,nspecies,nstate,double>::create_Functional(params.functional_param, flow_solver_FOM->dg);
62  this->pcout << "FUNCTIONAL FROM FOM" << std::endl;
63  this->pcout << functional_FOM->evaluate_functional(false, false) << std::endl;
64 
65  solveFunctionalROM(functional_ROM);
66 
68 
69  this->outputIterationData(std::to_string(iteration));
70  timer.leave_subsection();
71  timer.enter_subsection ("Iteration " + std::to_string(iteration+1));
72 
73  this->pcout << "Sampling snapshot at " << max_error_params << std::endl;
74  dealii::LinearAlgebra::distributed::Vector<double> fom_solution = this->solveSnapshotFOM(max_error_params);
75  this->snapshot_parameters.conservativeResize(this->snapshot_parameters.rows()+1, this->snapshot_parameters.cols());
76  this->snapshot_parameters.row(this->snapshot_parameters.rows()-1) = max_error_params;
77  this->nearest_neighbors->update_snapshots(this->snapshot_parameters, fom_solution);
78  this->current_pod->addSnapshot(fom_solution);
79  this->fom_locations.emplace_back(fom_solution);
80  this->current_pod->computeBasis();
81 
82  // Update previous ROM errors with updated this->current_pod
83  for(auto it = this->rom_locations.begin(); it != this->rom_locations.end(); ++it){
84  it->get()->compute_initial_rom_to_final_rom_error(this->current_pod);
85  it->get()->compute_total_error();
86  }
87 
88  updateNearestExistingROMs(max_error_params);
89 
90  rom_points = this->nearest_neighbors->kNearestNeighborsMidpoint(max_error_params);
91  this->pcout << rom_points << std::endl;
92 
93  placeROMLocations(rom_points);
94 
95  // Update max error
96  max_error_params = this->getMaxErrorROM();
97 
98  this->pcout << "Max error is: " << this->max_error << std::endl;
99 
100  solveFunctionalROM(functional_ROM);
101 
102  this->pcout << "FUNCTIONAL FROM ROMs" << std::endl;
103  std::ofstream output_file("rom_functional" + std::to_string(iteration+1) +".txt");
104 
105  std::ostream_iterator<double> output_iterator(output_file, "\n");
106  std::copy(std::begin(rom_functional), std::end(rom_functional), output_iterator);
107 
108  iteration++;
109  }
110 
111  this->outputIterationData("final");
112 
113  timer.leave_subsection();
114 
115  this->pcout << "FUNCTIONAL FROM ROMs" << std::endl;
116  std::ofstream output_file("rom_functional.txt");
117 
118  std::ostream_iterator<double> output_iterator(output_file, "\n");
119  std::copy(std::begin(rom_functional), std::end(rom_functional), output_iterator);
120 
121  return 0;
122 }
123 
124 template <int dim, int nspecies, int nstate>
125 bool AdaptiveSampling<dim, nspecies, nstate>::placeROMLocations(const MatrixXd& rom_points) const{
126  bool error_greater_than_tolerance = false;
127  for(auto midpoint : rom_points.rowwise()){
128 
129  // Check if ROM point already exists as another ROM point
130  auto element = std::find_if(this->rom_locations.begin(), this->rom_locations.end(), [&midpoint](std::unique_ptr<ProperOrthogonalDecomposition::ROMTestLocation<dim,nspecies,nstate>>& location){ return location->parameter.isApprox(midpoint);} );
131 
132  // Check if ROM point already exists as a snapshot
133  bool snapshot_exists = false;
134  for(auto snap_param : this->snapshot_parameters.rowwise()){
135  if(snap_param.isApprox(midpoint)){
136  snapshot_exists = true;
137  }
138  }
139 
140  if(element == this->rom_locations.end() && snapshot_exists == false){
141  std::unique_ptr<ProperOrthogonalDecomposition::ROMSolution<dim, nspecies, nstate>> rom_solution = solveSnapshotROM(midpoint);
142  this->rom_locations.emplace_back(std::make_unique<ProperOrthogonalDecomposition::ROMTestLocation<dim,nspecies,nstate>>(midpoint, std::move(rom_solution)));
143  if(abs(this->rom_locations.back()->total_error) > this->all_parameters->reduced_order_param.adaptation_tolerance){
144  error_greater_than_tolerance = true;
145  }
146  }
147  else{
148  this->pcout << "ROM already computed." << std::endl;
149  }
150  }
151  return error_greater_than_tolerance;
152 }
153 
154 template <int dim, int nspecies, int nstate>
155 void AdaptiveSampling<dim, nspecies, nstate>::trueErrorROM(const MatrixXd& rom_points) const{
156 
157  std::unique_ptr<dealii::TableHandler> rom_table = std::make_unique<dealii::TableHandler>();
158 
159  for(auto rom : rom_points.rowwise()){
160  for(int i = 0 ; i < rom_points.cols() ; i++){
161  rom_table->add_value(this->all_parameters->reduced_order_param.parameter_names[i], rom(i));
162  rom_table->set_precision(this->all_parameters->reduced_order_param.parameter_names[i], 16);
163  }
164  double error = solveSnapshotROMandFOM(rom);
165  this->pcout << "Error in the functional: " << error << std::endl;
166  rom_table->add_value("ROM_errors", error);
167  rom_table->set_precision("ROM_errors", 16);
168  }
169 
170  std::ofstream rom_table_file("true_error_table_iteration_ROM_post_sampling.txt");
171  rom_table->write_text(rom_table_file, dealii::TableHandler::TextOutputFormat::org_mode_table);
172  rom_table_file.close();
173 }
174 
175 template <int dim, int nspecies, int nstate>
176 double AdaptiveSampling<dim, nspecies, nstate>::solveSnapshotROMandFOM(const RowVectorXd& parameter) const{
177  this->pcout << "Solving ROM at " << parameter << std::endl;
178  Parameters::AllParameters params = this->reinit_params(parameter);
179 
180  std::unique_ptr<FlowSolver::FlowSolver<dim,nspecies,nstate>> flow_solver_ROM = FlowSolver::FlowSolverFactory<dim,nspecies,nstate>::select_flow_case(&params, this->parameter_handler);
181 
182  // Solve implicit solution
183  auto ode_solver_type_ROM = Parameters::ODESolverParam::ODESolverEnum::pod_petrov_galerkin_solver;
184  flow_solver_ROM->ode_solver = PHiLiP::ODE::ODESolverFactory<dim, nspecies, double>::create_ODESolver_manual(ode_solver_type_ROM, flow_solver_ROM->dg, this->current_pod);
185  flow_solver_ROM->ode_solver->allocate_ode_system();
186  flow_solver_ROM->ode_solver->steady_state();
187 
188  this->pcout << "Done solving ROM." << std::endl;
189 
190  // Create functional
191  std::shared_ptr<Functional<dim,nspecies,nstate,double>> functional_ROM = FunctionalFactory<dim,nspecies,nstate,double>::create_Functional(params.functional_param, flow_solver_ROM->dg);
192 
193  this->pcout << "Solving FOM at " << parameter << std::endl;
194 
195  std::unique_ptr<FlowSolver::FlowSolver<dim,nspecies,nstate>> flow_solver_FOM = FlowSolver::FlowSolverFactory<dim,nspecies,nstate>::select_flow_case(&params, this->parameter_handler);
196 
197  // Solve implicit solution
198  auto ode_solver_type = Parameters::ODESolverParam::ODESolverEnum::implicit_solver;
199  flow_solver_FOM->ode_solver = PHiLiP::ODE::ODESolverFactory<dim, nspecies, double>::create_ODESolver_manual(ode_solver_type, flow_solver_FOM->dg);
200  flow_solver_FOM->ode_solver->allocate_ode_system();
201  flow_solver_FOM->run();
202 
203  // Create functional
204  std::shared_ptr<Functional<dim,nspecies,nstate,double>> functional_FOM = FunctionalFactory<dim,nspecies,nstate,double>::create_Functional(params.functional_param, flow_solver_FOM->dg);
205 
206  this->pcout << "Done solving FOM." << std::endl;
207  return functional_ROM->evaluate_functional(false, false) - functional_FOM->evaluate_functional(false, false);
208 }
209 
210 template <int dim, int nspecies, int nstate>
211 void AdaptiveSampling<dim, nspecies, nstate>::solveFunctionalROM(const RowVectorXd& parameter) const{
212  this->pcout << "Solving ROM at " << parameter << std::endl;
213  Parameters::AllParameters params = this->reinit_params(parameter);
214 
215  std::unique_ptr<FlowSolver::FlowSolver<dim,nspecies,nstate>> flow_solver_ROM = FlowSolver::FlowSolverFactory<dim,nspecies,nstate>::select_flow_case(&params, this->parameter_handler);
216 
217  // Solve implicit solution
218  auto ode_solver_type_ROM = Parameters::ODESolverParam::ODESolverEnum::pod_petrov_galerkin_solver;
219  flow_solver_ROM->ode_solver = PHiLiP::ODE::ODESolverFactory<dim, nspecies, double>::create_ODESolver_manual(ode_solver_type_ROM, flow_solver_ROM->dg, this->current_pod);
220  flow_solver_ROM->ode_solver->allocate_ode_system();
221  flow_solver_ROM->ode_solver->steady_state();
222 
223  this->pcout << "Done solving ROM." << std::endl;
224 
225  // Create functional
226  std::shared_ptr<Functional<dim,nspecies,nstate,double>> functional_ROM = FunctionalFactory<dim,nspecies,nstate,double>::create_Functional(params.functional_param, flow_solver_ROM->dg);
227 
228  rom_functional.emplace_back(functional_ROM->evaluate_functional(false, false));
229 }
230 
231 template <int dim, int nspecies, int nstate>
232 void AdaptiveSampling<dim, nspecies, nstate>::updateNearestExistingROMs(const RowVectorXd& /*parameter*/) const{
233 
234  this->pcout << "Verifying ROM points for recomputation." << std::endl;
235  // Assemble ROM points in a matrix
236  MatrixXd rom_points(0,0);
237  for(auto it = this->rom_locations.begin(); it != this->rom_locations.end(); ++it){
238  rom_points.conservativeResize(rom_points.rows()+1, it->get()->parameter.cols());
239  rom_points.row(rom_points.rows()-1) = it->get()->parameter;
240  }
241 
242  // Get distances between each ROM point and all other ROM points
243  for(auto point : rom_points.rowwise()) {
245  MatrixXd scaled_rom_points = scaler.fit_transform(rom_points);
246  RowVectorXd scaled_point = scaler.transform(point);
247 
248  VectorXd distances = (scaled_rom_points.rowwise() - scaled_point).rowwise().squaredNorm();
249 
250  std::vector<int> index(distances.size());
251  std::iota(index.begin(), index.end(), 0);
252 
253  std::sort(index.begin(), index.end(),
254  [&](const int &a, const int &b) {
255  return distances[a] < distances[b];
256  });
257 
258  this->pcout << "Searching ROM points near: " << point << std::endl;
259  double local_mean_error = 0;
260  for (int i = 1; i < rom_points.cols() + 2; i++) {
261  local_mean_error = local_mean_error + std::abs(this->rom_locations[index[i]]->total_error);
262  }
263  local_mean_error = local_mean_error / (rom_points.cols() + 1);
264  if ((std::abs(this->rom_locations[index[0]]->total_error) > this->all_parameters->reduced_order_param.recomputation_coefficient * local_mean_error) || (std::abs(this->rom_locations[index[0]]->total_error) < (1/this->all_parameters->reduced_order_param.recomputation_coefficient) * local_mean_error)) {
265  this->pcout << "Total error greater than tolerance. Recomputing ROM solution" << std::endl;
266  std::unique_ptr<ProperOrthogonalDecomposition::ROMSolution<dim, nspecies, nstate>> rom_solution = solveSnapshotROM(this->rom_locations[index[0]]->parameter);
267  std::unique_ptr<ProperOrthogonalDecomposition::ROMTestLocation<dim, nspecies, nstate>> rom_location = std::make_unique<ProperOrthogonalDecomposition::ROMTestLocation<dim, nspecies, nstate>>(this->rom_locations[index[0]]->parameter, std::move(rom_solution));
268  this->rom_locations[index[0]] = std::move(rom_location);
269  }
270  }
271 }
272 
273 template <int dim, int nspecies, int nstate>
274 std::unique_ptr<ProperOrthogonalDecomposition::ROMSolution<dim,nspecies,nstate>> AdaptiveSampling<dim, nspecies, nstate>::solveSnapshotROM(const RowVectorXd& parameter) const{
275  this->pcout << "Solving ROM at " << parameter << std::endl;
276  Parameters::AllParameters params = this->reinit_params(parameter);
277 
278  std::unique_ptr<FlowSolver::FlowSolver<dim,nspecies,nstate>> flow_solver = FlowSolver::FlowSolverFactory<dim,nspecies,nstate>::select_flow_case(&params, this->parameter_handler);
279 
280  // Solve implicit solution
281  auto ode_solver_type = Parameters::ODESolverParam::ODESolverEnum::pod_petrov_galerkin_solver;
282  flow_solver->ode_solver = PHiLiP::ODE::ODESolverFactory<dim, nspecies, double>::create_ODESolver_manual(ode_solver_type, flow_solver->dg, this->current_pod);
283  flow_solver->ode_solver->allocate_ode_system();
284  flow_solver->ode_solver->steady_state();
285 
286  // Create functional
287  std::shared_ptr<Functional<dim,nspecies,nstate,double>> functional = FunctionalFactory<dim,nspecies,nstate,double>::create_Functional(params.functional_param, flow_solver->dg);
288  functional->evaluate_functional( true, false, false);
289 
290  dealii::LinearAlgebra::distributed::Vector<double> solution(flow_solver->dg->solution);
291  dealii::LinearAlgebra::distributed::Vector<double> gradient(functional->dIdw);
292 
293  std::unique_ptr<ProperOrthogonalDecomposition::ROMSolution<dim,nspecies,nstate>> rom_solution = std::make_unique<ProperOrthogonalDecomposition::ROMSolution<dim, nspecies, nstate>>(params, solution, gradient);
294  this->pcout << "Done solving ROM." << std::endl;
295 
296  return rom_solution;
297 }
298 
299 #if PHILIP_DIM==1 && PHILIP_SPECIES==1
301 #endif
302 
303 #if PHILIP_DIM!=1 && PHILIP_SPECIES==1
305 #endif
306 
307 }
std::vector< double > rom_functional
Functional value predicted by the rom at each sammpling iteration at parameter location specified in ...
std::vector< std::string > parameter_names
Names of parameters.
int run_sampling() const override
Run Sampling Procedure.
POD adaptive sampling.
double solveSnapshotROMandFOM(const RowVectorXd &parameter) const
Solve FOM and ROM, return error in functional between the models.
Files for the baseline physics.
Definition: ADTypes.hpp:10
dealii::LinearAlgebra::distributed::Vector< double > solveSnapshotFOM(const RowVectorXd &parameter) const
Solve full-order snapshot.
void placeInitialSnapshots() const
Placement of initial snapshots.
MatrixXd snapshot_parameters
Matrix of snapshot parameters.
static std::shared_ptr< ODESolverBase< dim, nspecies, real, MeshType > > create_ODESolver_manual(Parameters::ODESolverParam::ODESolverEnum ode_solver_type, std::shared_ptr< DGBase< dim, nspecies, real, MeshType > > dg_input)
Creates either implicit or explicit ODE solver based on manual input (no POD basis given) ...
ReducedOrderModelParam reduced_order_param
Contains parameters for the Reduced-Order model.
static std::unique_ptr< FlowSolver< dim, nspecies, 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.
const Parameters::AllParameters *const all_parameters
Pointer to all parameters.
dealii::ConditionalOStream pcout
ConditionalOStream.
Main parameter class that contains the various other sub-parameter classes.
Class to compute and store adjoint-based error estimates.
MatrixXd fit_transform(const MatrixXd &snapshot_parameters)
Fit and transform data.
AdaptiveSampling(const PHiLiP::Parameters::AllParameters *const parameters_input, const dealii::ParameterHandler &parameter_handler_input)
Constructor.
void updateNearestExistingROMs(const RowVectorXd &parameter) const
Updates nearest ROM points to snapshot if error discrepancy is above tolerance.
int recomputation_coefficient
Recomputation parameter for adaptive sampling algorithm.
virtual void outputIterationData(std::string iteration) const
Output for each iteration.
std::vector< dealii::LinearAlgebra::distributed::Vector< double > > fom_locations
Vector of parameter-ROMTestLocation pairs.
const dealii::ParameterHandler & parameter_handler
Parameter handler for storing the .prm file being ran.
std::vector< std::unique_ptr< ProperOrthogonalDecomposition::ROMTestLocation< dim, nspecies, nstate > > > rom_locations
Vector of parameter-ROMTestLocation pairs.
static std::shared_ptr< Functional< dim, nspecies, nstate, real, MeshType > > create_Functional(PHiLiP::Parameters::AllParameters const *const param, std::shared_ptr< PHiLiP::DGBase< dim, nspecies, real, MeshType > > dg)
Create standard functional object from constant parameter file.
std::unique_ptr< ProperOrthogonalDecomposition::ROMSolution< dim, nspecies, nstate > > solveSnapshotROM(const RowVectorXd &parameter) const
Solve reduced-order solution.
void trueErrorROM(const MatrixXd &rom_points) const
Compute true/actual error at all ROM points (error in functional between FOM and ROM solution) ...
RowVectorXd readROMFunctionalPoint() const
Find point to solve for functional from param file.
virtual RowVectorXd getMaxErrorROM() const
Compute RBF and find max error.
MatrixXd transform(const MatrixXd &snapshot_parameters)
Transform data to previously fitted dataset.
std::shared_ptr< ProperOrthogonalDecomposition::OnlinePOD< dim, nspecies > > current_pod
Most up to date POD basis.
bool placeROMLocations(const MatrixXd &rom_points) const
Placement of ROMs.
FunctionalParam functional_param
Contains parameters for functional.
void solveFunctionalROM(const RowVectorXd &parameter) const
Solve ROM and track functional.
double adaptation_tolerance
Tolerance for POD adaptation.
std::shared_ptr< ProperOrthogonalDecomposition::NearestNeighbors > nearest_neighbors
Nearest neighbors of snapshots.