[P]arallel [Hi]gh-order [Li]brary for [P]DEs  Latest
Parallel High-Order Library for PDEs through hp-adaptive Discontinuous Galerkin methods
hyper_reduction_comparison.cpp
1 #include "hyper_reduction_comparison.h"
2 #include "reduced_order/pod_basis_offline.h"
3 #include "parameters/all_parameters.h"
4 #include "flow_solver/flow_solver.h"
5 #include "flow_solver/flow_solver_factory.h"
6 #include "ode_solver/ode_solver_factory.h"
7 #include "reduced_order/assemble_ECSW_residual.h"
8 #include "reduced_order/assemble_ECSW_jacobian.h"
9 #include "linear_solver/NNLS_solver.h"
10 #include "linear_solver/helper_functions.h"
11 #include "reduced_order/pod_adaptive_sampling.h"
12 #include "rom_import_helper_functions.h"
13 #include <eigen/Eigen/Dense>
14 #include <iostream>
15 #include <filesystem>
16 
17 namespace PHiLiP {
18 namespace Tests {
19 
20 template <int dim, int nstate>
22  const dealii::ParameterHandler &parameter_handler_input)
23  : TestsBase::TestsBase(parameters_input)
24  , parameter_handler(parameter_handler_input)
25 {}
26 
27 template <int dim, int nstate>
29  // Copy all parameters
31 
32  parameters.ode_solver_param.nonlinear_max_iterations = max_iter;
33  return parameters;
34 }
35 
36 template <int dim, int nstate>
38  bool file_found = false;
39  Epetra_MpiComm epetra_comm(MPI_COMM_WORLD);
40  VectorXd weights_eig;
41  int rows = 0;
43 
44  std::vector<std::filesystem::path> files_in_directory;
45  std::copy(std::filesystem::directory_iterator(path), std::filesystem::directory_iterator(), std::back_inserter(files_in_directory));
46  std::sort(files_in_directory.begin(), files_in_directory.end()); //Sort files so that the order is the same as for the sensitivity basis
47 
48  for (const auto & entry : files_in_directory){
49  if(std::string(entry.filename()).std::string::find("weights") != std::string::npos){
50  pcout << "Processing " << entry << std::endl;
51  file_found = true;
52  std::ifstream myfile(entry);
53  if(!myfile)
54  {
55  pcout << "Error opening file." << std::endl;
56  std::abort();
57  }
58  std::string line;
59 
60  while(std::getline(myfile, line)){ //for each line
61  std::istringstream stream(line);
62  std::string field;
63  while (getline(stream, field,' ')) { //parse data values on each line
64  if (field.empty()) {
65  continue;
66  }
67  else {
68  try{
69  std::stod(field);
70  rows++;
71  } catch (...){
72  continue;
73  }
74  }
75  }
76  }
77 
78  weights_eig.resize(rows);
79  int row = 0;
80  myfile.clear();
81  myfile.seekg(0); //Bring back to beginning of file
82  //Second loop set to build solutions matrix
83  while(std::getline(myfile, line)){ //for each line
84  std::istringstream stream(line);
85  std::string field;
86  while (getline(stream, field,' ')) { //parse data values on each line
87  if (field.empty()) {
88  continue;
89  }
90  else {
91  try{
92  double num_string = std::stod(field);
93  weights_eig(row) = num_string;
94  row++;
95  } catch (...){
96  continue;
97  }
98  }
99  }
100  }
101  myfile.close();
102  }
103  }
104 
105  Epetra_CrsMatrix epetra_system_matrix = dg->system_matrix.trilinos_matrix();
106  const int n_quad_pts = dg->volume_quadrature_collection[dg->all_parameters->flow_solver_param.poly_degree].size();
107  const int length = epetra_system_matrix.NumMyRows()/(nstate*n_quad_pts);
108  int *local_elements = new int[length];
109  int ctr = 0;
110  for (const auto &cell : dg->dof_handler.active_cell_iterators())
111  {
112  if (cell->is_locally_owned()){
113  local_elements[ctr] = cell->active_cell_index();
114  ctr +=1;
115  }
116  }
117 
118  Epetra_Map ColMap(rows, length, local_elements, 0, epetra_comm);
119  ColMap.Print(std::cout);
120  Epetra_Vector weights(ColMap);
121  for(int i = 0; i < length; i++){
122  int global_ind = local_elements[i];
123  weights[i] = weights_eig(global_ind);
124  }
125 
126  ptr_weights = std::make_shared<Epetra_Vector>(weights);
127  return file_found;
128 }
129 
130 template <int dim, int nstate>
132 {
133  pcout << "Starting error evaluation for ROM and HROM at one parameter location..." << std::endl;
134 
135  Epetra_MpiComm Comm( MPI_COMM_WORLD );
136 
137  // Create implicit solver for comparison
138  std::unique_ptr<FlowSolver::FlowSolver<dim,nstate>> flow_solver_implicit = FlowSolver::FlowSolverFactory<dim,nstate>::select_flow_case(all_parameters, parameter_handler);
139  auto functional_implicit = FunctionalFactory<dim,nstate,double>::create_Functional(all_parameters->functional_param, flow_solver_implicit->dg);
140 
141  // Create POD Petrov-Galerkin ROM without Hyper-reduction
142  std::unique_ptr<FlowSolver::FlowSolver<dim,nstate>> flow_solver_petrov_galerkin = FlowSolver::FlowSolverFactory<dim,nstate>::select_flow_case(all_parameters, parameter_handler);
143 
144  // Create POD Petrov-Galerkin ROM with Hyper-reduction
145  Parameters::AllParameters new_parameters = reinit_params(100);
146  std::unique_ptr<FlowSolver::FlowSolver<dim,nstate>> flow_solver_hyper_reduced_petrov_galerkin = FlowSolver::FlowSolverFactory<dim,nstate>::select_flow_case(&new_parameters, parameter_handler);
147  auto ode_solver_type = Parameters::ODESolverParam::ODESolverEnum::hyper_reduced_petrov_galerkin_solver;
148 
149  // Run Adaptive Sampling to choose snapshot locations or load from file
150  std::shared_ptr<AdaptiveSampling<dim,nstate>> parameter_sampling = std::make_unique<AdaptiveSampling<dim,nstate>>(all_parameters, parameter_handler);
151  bool exit_con;
152  if (this->all_parameters->hyper_reduction_param.adapt_sampling_bool) {
153  parameter_sampling->run_sampling();
154 
155  // Find C and d for NNLS Problem
156  pcout << "Construct instance of Assembler..."<< std::endl;
157  std::shared_ptr<HyperReduction::AssembleECSWBase<dim,nstate>> constructor_NNLS_problem;
158  if (this->all_parameters->hyper_reduction_param.training_data == "residual")
159  constructor_NNLS_problem = std::make_shared<HyperReduction::AssembleECSWRes<dim,nstate>>(all_parameters, parameter_handler, flow_solver_hyper_reduced_petrov_galerkin->dg, parameter_sampling->current_pod, parameter_sampling->snapshot_parameters, ode_solver_type, Comm);
160  else {
161  constructor_NNLS_problem = std::make_shared<HyperReduction::AssembleECSWJac<dim,nstate>>(all_parameters, parameter_handler, flow_solver_hyper_reduced_petrov_galerkin->dg, parameter_sampling->current_pod, parameter_sampling->snapshot_parameters, ode_solver_type, Comm);
162  }
163  pcout << "Build Problem..."<< std::endl;
164  constructor_NNLS_problem->build_problem();
165 
166  // Transfer b vector (RHS of NNLS problem) to Epetra structure
167  // bMap is the same map of b from the constructer_NNLS_problem, when the vector is allocated onto on core
168  const int rank = Comm.MyPID();
169  int rows = (constructor_NNLS_problem->A_T->trilinos_matrix()).NumGlobalCols();
170  Epetra_Map bMap(rows, (rank == 0) ? rows: 0, 0, Comm);
171  Epetra_Vector b_Epetra(bMap);
172  auto b = constructor_NNLS_problem->b;
173  unsigned int local_length = bMap.NumMyElements();
174  for(unsigned int i = 0 ; i < local_length ; i++){
175  b_Epetra[i] = b(i);
176  }
177 
178  // Solve NNLS Problem for ECSW weights
179  pcout << "Create NNLS problem..."<< std::endl;
180  NNLSSolver NNLS_prob(all_parameters, parameter_handler, constructor_NNLS_problem->A_T->trilinos_matrix(), true, Comm, b_Epetra);
181  pcout << "Solve NNLS problem..."<< std::endl;
182  exit_con = NNLS_prob.solve();
183  pcout << exit_con << std::endl;
184 
185  *ptr_weights = NNLS_prob.get_solution();
186  pcout << "ECSW Weights"<< std::endl;
187  pcout << *ptr_weights << std::endl;
188  }
189  else{
190  exit_con = true;
191  snapshot_parameters(0,0);
192  std::string path = all_parameters->reduced_order_param.path_to_search; //Search specified directory for files containing "solutions_table"
193  bool snap_found = getSnapshotParamsFromFile(snapshot_parameters, path);
194  if (snap_found){
195  parameter_sampling->snapshot_parameters = snapshot_parameters;
196  }
197  else{
198  pcout << "File with snapshots not found in folder" << std::endl;
199  return -1;
200  }
201  std::shared_ptr<ProperOrthogonalDecomposition::OfflinePOD<dim>> pod_petrov_galerkin = std::make_shared<ProperOrthogonalDecomposition::OfflinePOD<dim>>(flow_solver_petrov_galerkin->dg);
202  parameter_sampling->current_pod->basis = pod_petrov_galerkin->basis;
203  parameter_sampling->current_pod->referenceState = pod_petrov_galerkin->referenceState;
204  parameter_sampling->current_pod->snapshotMatrix = pod_petrov_galerkin->snapshotMatrix;
205 
206  bool weights_found = getWeightsFromFile(flow_solver_hyper_reduced_petrov_galerkin->dg);
207  if (weights_found){
208  pcout << "ECSW Weights" << std::endl;
209  pcout << *ptr_weights << std::endl;
210  }
211  else{
212  pcout << "File with weights not found in folder" << std::endl;
213  return -1;
214  }
215  }
216  MatrixXd snapshot_parameters = parameter_sampling->snapshot_parameters;
217 
218  // Build ODE for POD Petrov-Galerkin
219  ode_solver_type = Parameters::ODESolverParam::ODESolverEnum::pod_petrov_galerkin_solver;
220  flow_solver_petrov_galerkin->ode_solver = PHiLiP::ODE::ODESolverFactory<dim, double>::create_ODESolver_manual(ode_solver_type, flow_solver_petrov_galerkin->dg, parameter_sampling->current_pod);
221  flow_solver_petrov_galerkin->ode_solver->allocate_ode_system();
222  auto functional_petrov_galerkin = FunctionalFactory<dim,nstate,double>::create_Functional(all_parameters->functional_param, flow_solver_petrov_galerkin->dg);
223 
224  // Build ODE for Hyper-Reduced POD Petrov-Galerkin
225  ode_solver_type = Parameters::ODESolverParam::ODESolverEnum::hyper_reduced_petrov_galerkin_solver;
226  flow_solver_hyper_reduced_petrov_galerkin->ode_solver = PHiLiP::ODE::ODESolverFactory<dim, double>::create_ODESolver_manual(ode_solver_type, flow_solver_hyper_reduced_petrov_galerkin->dg, parameter_sampling->current_pod, *ptr_weights);
227  flow_solver_hyper_reduced_petrov_galerkin->ode_solver->allocate_ode_system();
228  auto functional_hyper_reduced_petrov_galerkin = FunctionalFactory<dim,nstate,double>::create_Functional(all_parameters->functional_param, flow_solver_hyper_reduced_petrov_galerkin->dg);
229 
230  pcout << "Implicit Solve Results"<< std::endl;
231  flow_solver_implicit->run();
232  pcout << "PG Solve Results"<< std::endl;
233  flow_solver_petrov_galerkin->ode_solver->steady_state();
234  pcout << "Hyper Reduced PG Solve Results"<< std::endl;
235  flow_solver_hyper_reduced_petrov_galerkin->ode_solver->steady_state();
236 
237  // Extract Solutions
238  dealii::LinearAlgebra::distributed::Vector<double> implicit_solution(flow_solver_implicit->dg->solution);
239  dealii::LinearAlgebra::distributed::Vector<double> petrov_galerkin_solution(flow_solver_petrov_galerkin->dg->solution);
240  dealii::LinearAlgebra::distributed::Vector<double> hyper_reduced_petrov_galerkin_solution(flow_solver_hyper_reduced_petrov_galerkin->dg->solution);
241 
242  // Write solution vectors to text files
243  dealii::LinearAlgebra::ReadWriteVector<double> write_implicit_solution(flow_solver_implicit->dg->solution.size());
244  write_implicit_solution.import(flow_solver_implicit->dg->solution, dealii::VectorOperation::values::insert);
245  std::ofstream out_file_imp("implicit_solution.txt");
246  for(unsigned int i = 0 ; i < write_implicit_solution.size() ; i++){
247  out_file_imp << " " << std::setprecision(17) << write_implicit_solution(i) << " \n";
248  }
249  out_file_imp.close();
250 
251  dealii::LinearAlgebra::ReadWriteVector<double> write_pg_solution(flow_solver_petrov_galerkin->dg->solution.size());
252  write_pg_solution.import(flow_solver_petrov_galerkin->dg->solution, dealii::VectorOperation::values::insert);
253  std::ofstream out_file_pg("pg_solution.txt");
254  for(unsigned int i = 0 ; i < write_pg_solution.size() ; i++){
255  out_file_pg << " " << std::setprecision(17) << write_pg_solution(i) << " \n";
256  }
257  out_file_pg.close();
258 
259  dealii::LinearAlgebra::ReadWriteVector<double> write_hyp_solution(flow_solver_hyper_reduced_petrov_galerkin->dg->solution.size());
260  write_hyp_solution.import(flow_solver_hyper_reduced_petrov_galerkin->dg->solution, dealii::VectorOperation::values::insert);
261  std::ofstream out_file_hyp("hyp_solution.txt");
262  for(unsigned int i = 0 ; i < write_hyp_solution.size() ; i++){
263  out_file_hyp << " " << std::setprecision(17) << write_hyp_solution(i) << " \n";
264  }
265  out_file_hyp.close();
266 
267  // Check errors in the solution and the functional
268  double petrov_galerkin_solution_error = ((petrov_galerkin_solution-=implicit_solution).l2_norm()/implicit_solution.l2_norm());
269  double hyper_reduced_solution_error = ((hyper_reduced_petrov_galerkin_solution-=implicit_solution).l2_norm()/implicit_solution.l2_norm());
270 
271  double petrov_galerkin_func_error = functional_petrov_galerkin->evaluate_functional(false,false) - functional_implicit->evaluate_functional(false,false);
272  double hyper_reduced_func_error = functional_hyper_reduced_petrov_galerkin->evaluate_functional(false,false) - functional_implicit->evaluate_functional(false,false);
273 
274  pcout << "Petrov-Galerkin solution error: " << petrov_galerkin_solution_error << std::endl
275  << "Petrov-Galerkin functional error: " << petrov_galerkin_func_error << std::endl;
276 
277  pcout << "Hyper-Reduced Petrov-Galerkin solution error: " << hyper_reduced_solution_error << std::endl
278  << "Hyper-Reduced Petrov-Galerkin functional error: " << hyper_reduced_func_error << std::endl;
279 
280  if (std::abs(petrov_galerkin_solution_error) < 1E-6 && std::abs(petrov_galerkin_func_error) < 1E-4 && exit_con){
281  pcout << "Passed!";
282  return 0;
283  }else{
284  pcout << "Failed!";
285  return -1;
286  }
287 }
288 
289 #if PHILIP_DIM==1
291 #endif
292 
293 #if PHILIP_DIM!=1
295 #endif
296 } // Tests namespace
297 } // PHiLiP namespace
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) ...
HyperReductionComparison(const Parameters::AllParameters *const parameters_input, const dealii::ParameterHandler &parameter_handler_input)
Constructor.
Files for the baseline physics.
Definition: ADTypes.hpp:10
bool getWeightsFromFile(std::shared_ptr< DGBase< dim, double >> &dg) const
Read ECSW weights from the text file.
ReducedOrderModelParam reduced_order_param
Contains parameters for the Reduced-Order model.
std::string path_to_search
Path to search for snapshots or saved POD basis.
Main parameter class that contains the various other sub-parameter classes.
ODESolverParam ode_solver_param
Contains parameters for ODE solver.
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.
const Parameters::AllParameters *const all_parameters
Pointer to all parameters.
Definition: tests.h:20
Parameters::AllParameters reinit_params(const int max_iter) const
Reinitialize parameters.
std::shared_ptr< Epetra_Vector > ptr_weights
Ptr vector of ECSW Weights.
const dealii::ParameterHandler & parameter_handler
Dummy parameter handler because flowsolver requires it.
MatrixXd snapshot_parameters
Matrix of snapshot parameters.
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.
FunctionalParam functional_param
Contains parameters for functional.
dealii::ConditionalOStream pcout
ConditionalOStream.
Definition: tests.h:45
DGBase is independent of the number of state variables.
Definition: dg_base.hpp:82
int run_test() const override
Build three models and evaluate error measures.
unsigned int nonlinear_max_iterations
Maximum number of iterations.
Base class of all the tests.
Definition: tests.h:17