[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 nstate>
25  const dealii::ParameterHandler &parameter_handler_input)
26  : AdaptiveSamplingBase<dim, nstate>(parameters_input, parameter_handler_input)
27 {}
28 
29 template <int dim, 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,nstate>> flow_solver_FOM = FlowSolver::FlowSolverFactory<dim,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, 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,nstate,double>> functional_FOM = FunctionalFactory<dim,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 nstate>
125 bool AdaptiveSampling<dim, 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,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, nstate>> rom_solution = solveSnapshotROM(midpoint);
142  this->rom_locations.emplace_back(std::make_unique<ProperOrthogonalDecomposition::ROMTestLocation<dim,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 nstate>
155 void AdaptiveSampling<dim, 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 nstate>
176 double AdaptiveSampling<dim, 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,nstate>> flow_solver_ROM = FlowSolver::FlowSolverFactory<dim,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, 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,nstate,double>> functional_ROM = FunctionalFactory<dim,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,nstate>> flow_solver_FOM = FlowSolver::FlowSolverFactory<dim,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, 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,nstate,double>> functional_FOM = FunctionalFactory<dim,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 nstate>
211 void AdaptiveSampling<dim, 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,nstate>> flow_solver_ROM = FlowSolver::FlowSolverFactory<dim,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, 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,nstate,double>> functional_ROM = FunctionalFactory<dim,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 nstate>
232 void AdaptiveSampling<dim, 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, nstate>> rom_solution = solveSnapshotROM(this->rom_locations[index[0]]->parameter);
267  std::unique_ptr<ProperOrthogonalDecomposition::ROMTestLocation<dim, nstate>> rom_location = std::make_unique<ProperOrthogonalDecomposition::ROMTestLocation<dim, 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 nstate>
274 std::unique_ptr<ProperOrthogonalDecomposition::ROMSolution<dim,nstate>> AdaptiveSampling<dim, 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,nstate>> flow_solver = FlowSolver::FlowSolverFactory<dim,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, 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,nstate,double>> functional = FunctionalFactory<dim,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,nstate>> rom_solution = std::make_unique<ProperOrthogonalDecomposition::ROMSolution<dim, nstate>>(params, solution, gradient);
294  this->pcout << "Done solving ROM." << std::endl;
295 
296  return rom_solution;
297 }
298 
299 #if PHILIP_DIM==1
301 #endif
302 
303 #if PHILIP_DIM!=1
305 #endif
306 
307 }
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) ...
void trueErrorROM(const MatrixXd &rom_points) const
Compute true/actual error at all ROM points (error in functional between FOM and ROM solution) ...
void updateNearestExistingROMs(const RowVectorXd &parameter) const
Updates nearest ROM points to snapshot if error discrepancy is above tolerance.
const dealii::ParameterHandler & parameter_handler
Parameter handler for storing the .prm file being ran.
std::vector< std::string > parameter_names
Names of parameters.
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.
std::unique_ptr< ProperOrthogonalDecomposition::ROMSolution< dim, nstate > > solveSnapshotROM(const RowVectorXd &parameter) const
Solve reduced-order solution.
POD adaptive sampling.
std::vector< double > rom_functional
Functional value predicted by the rom at each sammpling iteration at parameter location specified in ...
dealii::LinearAlgebra::distributed::Vector< double > solveSnapshotFOM(const RowVectorXd &parameter) const
Solve full-order snapshot.
Files for the baseline physics.
Definition: ADTypes.hpp:10
bool placeROMLocations(const MatrixXd &rom_points) const
Placement of ROMs.
ReducedOrderModelParam reduced_order_param
Contains parameters for the Reduced-Order model.
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.
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.
int recomputation_coefficient
Recomputation parameter for adaptive sampling algorithm.
Parameters::AllParameters reinit_params(const RowVectorXd &parameter) const
Reinitialize parameters.
std::shared_ptr< ProperOrthogonalDecomposition::OnlinePOD< dim > > current_pod
Most up to date POD basis.
virtual void outputIterationData(std::string iteration) const
Output for each iteration.
void placeInitialSnapshots() const
Placement of initial snapshots.
static std::shared_ptr< Functional< dim, nstate, real, MeshType > > create_Functional(PHiLiP::Parameters::AllParameters const *const param, std::shared_ptr< PHiLiP::DGBase< dim, real, MeshType > > dg)
Create standard functional object from constant parameter file.
double solveSnapshotROMandFOM(const RowVectorXd &parameter) const
Solve FOM and ROM, return error in functional between the models.
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.
FunctionalParam functional_param
Contains parameters for functional.
int run_sampling() const override
Run Sampling Procedure.
MatrixXd snapshot_parameters
Matrix of snapshot parameters.
double adaptation_tolerance
Tolerance for POD adaptation.
void solveFunctionalROM(const RowVectorXd &parameter) const
Solve ROM and track functional.
const Parameters::AllParameters *const all_parameters
Pointer to all parameters.
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.