diff --git a/src/Control.cc b/src/Control.cc index bd8f5ced..d2771135 100644 --- a/src/Control.cc +++ b/src/Control.cc @@ -2063,6 +2063,8 @@ void Control::setROMOptions(const boost::program_options::variables_map& vm) rom_pri_option.num_potbasis = vm["ROM.basis.number_of_potential_basis"].as(); rom_pri_option.pot_rom_file = vm["ROM.potential_rom_file"].as(); + + rom_pri_option.test_restart_file = vm["ROM.online.test_restart_file"].as(); } // onpe0 // synchronize all processors @@ -2079,6 +2081,7 @@ void Control::syncROMOptions() mmpi.bcast(rom_pri_option.restart_file_fmt, comm_global_); mmpi.bcast(rom_pri_option.basis_file, comm_global_); mmpi.bcast(rom_pri_option.pot_rom_file, comm_global_); + mmpi.bcast(rom_pri_option.test_restart_file, comm_global_); auto bcast_check = [](int mpirc) { if (mpirc != MPI_SUCCESS) diff --git a/src/Potentials.cc b/src/Potentials.cc index a54f0c47..8ec568a7 100644 --- a/src/Potentials.cc +++ b/src/Potentials.cc @@ -899,8 +899,9 @@ void Potentials::initBackground(Ions& ions) if (fabs(background_charge_) < 1.e-10) background_charge_ = 0.; } +#ifdef MGMOL_HAS_LIBROM void Potentials::evalIonDensityOnSamplePts( - Ions& ions, const std::vector &local_idx, std::vector &sampled_rhoc) + Ions& ions, const std::vector &local_idx, CAROM::Vector &sampled_rhoc) { Mesh* mymesh = Mesh::instance(); MGmol_MPI& mmpi = *(MGmol_MPI::instance()); @@ -918,9 +919,8 @@ void Potentials::evalIonDensityOnSamplePts( } // initialize output vector - sampled_rhoc.resize(local_idx.size()); - for (int d = 0; d < sampled_rhoc.size(); d++) - sampled_rhoc[d] = 0.0; + sampled_rhoc.setSize(local_idx.size()); + sampled_rhoc = 0.0; // Loop over ions for (auto& ion : ions.overlappingVL_ions()) @@ -936,9 +936,9 @@ void Potentials::evalIonDensityOnSamplePts( } void Potentials::initializeRadialDataOnSampledPts( - const Vector3D& position, const Species& sp, const std::vector &local_idx, std::vector &sampled_rhoc) + const Vector3D& position, const Species& sp, const std::vector &local_idx, CAROM::Vector &sampled_rhoc) { - assert(local_idx.size() == sampled_rhoc.size()); + assert(local_idx.size() == sampled_rhoc.dim()); Control& ct = *(Control::instance()); @@ -984,11 +984,12 @@ void Potentials::initializeRadialDataOnSampledPts( /* accumulate ion species density */ const double r = position.minimage(point, lattice, ct.bcPoisson); if (r < lrad) - sampled_rhoc[k] += sp.getRhoComp(r); + sampled_rhoc(k) += sp.getRhoComp(r); } return; } +#endif template void Potentials::setVxc( const double* const vxc, const int iterativeIndex); diff --git a/src/Potentials.h b/src/Potentials.h index 0ddd10fa..d580f7a0 100644 --- a/src/Potentials.h +++ b/src/Potentials.h @@ -18,6 +18,10 @@ #include #include +#ifdef MGMOL_HAS_LIBROM +#include "librom.h" +#endif + class Ions; class Species; template @@ -95,8 +99,10 @@ class Potentials void initializeSupersampledRadialDataOnMesh( const Vector3D& position, const Species& sp); +#ifdef MGMOL_HAS_LIBROM void initializeRadialDataOnSampledPts( - const Vector3D& position, const Species& sp, const std::vector &local_idx, std::vector &sampled_rhoc); + const Vector3D& position, const Species& sp, const std::vector &local_idx, CAROM::Vector &sampled_rhoc); +#endif public: Potentials(const bool vh_frozen = false); @@ -164,6 +170,8 @@ class Potentials const double getBackgroundCharge() const { return background_charge_; } + const double getIonicCharge() const { return ionic_charge_; } + /*! * initialize total potential as local pseudopotential */ @@ -201,7 +209,9 @@ class Potentials void initBackground(Ions& ions); void addBackgroundToRhoComp(); - void evalIonDensityOnSamplePts(Ions& ions, const std::vector &local_idx, std::vector &sampled_rhoc); +#ifdef MGMOL_HAS_LIBROM + void evalIonDensityOnSamplePts(Ions& ions, const std::vector &local_idx, CAROM::Vector &sampled_rhoc); +#endif #ifdef HAVE_TRICUBIC void readExternalPot(const string filename, const char type); diff --git a/src/read_config.cc b/src/read_config.cc index 4f3b34c1..5cc99362 100644 --- a/src/read_config.cc +++ b/src/read_config.cc @@ -433,6 +433,8 @@ void setupROMConfigOption(po::options_description &rom_cfg) ("ROM.basis.number_of_potential_basis", po::value()->default_value(-1), "Number of potential POD basis to build Hartree potential ROM operator.") ("ROM.potential_rom_file", po::value()->default_value(""), - "File name to save/load potential ROM operators."); + "File name to save/load potential ROM operators.") + ("ROM.online.test_restart_file", po::value()->default_value(""), + "File name to test online ROM."); } #endif diff --git a/src/rom_Control.h b/src/rom_Control.h index 8218bbb4..564ede67 100644 --- a/src/rom_Control.h +++ b/src/rom_Control.h @@ -33,6 +33,7 @@ enum class ROMVariable { ORBITALS, POTENTIAL, + DENSITY, NONE }; @@ -54,6 +55,9 @@ struct ROMPrivateOptions /* options for ROM building */ int num_potbasis = -1; std::string pot_rom_file = ""; + + /* options for online Poisson ROM */ + std::string test_restart_file = ""; }; #endif // ROM_CONTROL_H diff --git a/src/rom_workflows.cc b/src/rom_workflows.cc index 52ad6e47..64c962c3 100644 --- a/src/rom_workflows.cc +++ b/src/rom_workflows.cc @@ -66,6 +66,9 @@ void readRestartFiles(MGmolInterface *mgmol_) Control& ct = *(Control::instance()); Mesh* mymesh = Mesh::instance(); const pb::PEenv& myPEenv = mymesh->peenv(); + MGmol_MPI& mmpi = *(MGmol_MPI::instance()); + const int rank = mmpi.mypeGlobal(); + const int nprocs = mmpi.size(); ROMPrivateOptions rom_options = ct.getROMOptions(); /* type of variable we intend to run POD */ @@ -131,6 +134,9 @@ void readRestartFiles(MGmolInterface *mgmol_) /* we save hartree potential */ basis_generator.takeSample(pot.vh_rho()); break; + + case ROMVariable::DENSITY: + basis_prefix += "_density"; } } basis_generator.writeSnapshot(); @@ -202,11 +208,19 @@ void buildROMPoissonOperator(MGmolInterface *mgmol_) delete col; } // for (int c = 0; c < num_pot_basis; c++) - /* DEIM hyperreduction */ + /* hyperreduction */ CAROM::Matrix pot_rhs_rom(num_pot_basis, num_pot_basis, false); std::vector global_sampled_row(num_pot_basis), sampled_rows_per_proc(nprocs); - DEIM(pot_basis, num_pot_basis, global_sampled_row, sampled_rows_per_proc, - pot_rhs_rom, rank, nprocs); + CAROM::Hyperreduction HR("deim"); + HR.ComputeSamples(pot_basis, num_pot_basis, global_sampled_row, sampled_rows_per_proc, + pot_rhs_rom, rank, nprocs, num_pot_basis); + if (rank == 0) + { + int num_sample_rows = 0; + for (int k = 0; k < sampled_rows_per_proc.size(); k++) + num_sample_rows += sampled_rows_per_proc[k]; + printf("number of sampled row: %d\n", num_sample_rows); + } /* ROM rescaleTotalCharge operator */ CAROM::Vector fom_ones(pot_basis->numRows(), true); @@ -230,6 +244,14 @@ void buildROMPoissonOperator(MGmolInterface *mgmol_) h5_helper.putDoubleArray("potential_rom_inverse", pot_rom.getData(), num_pot_basis * num_pot_basis, false); + /* save right-hand side hyper-reduction sample index */ + h5_helper.putIntegerArray("potential_rhs_hr_idx", global_sampled_row.data(), + global_sampled_row.size(), false); + h5_helper.putIntegerArray("potential_rhs_hr_idcs_per_proc", sampled_rows_per_proc.data(), + sampled_rows_per_proc.size(), false); + h5_helper.putInteger("potential_rhs_nprocs", nprocs); + h5_helper.putInteger("potential_rhs_hr_idx_size", global_sampled_row.size()); + /* save right-hand side hyper-reduction operator */ h5_helper.putDoubleArray("potential_rhs_rom_inverse", pot_rhs_rom.getData(), num_pot_basis * num_pot_basis, false); @@ -261,6 +283,19 @@ void runPoissonROM(MGmolInterface *mgmol_) MPI_Abort(MPI_COMM_WORLD, 0); } + /* Load MGmol pointer and Potentials */ + MGmol *mgmol = static_cast *>(mgmol_); + Poisson *poisson = mgmol->electrostat_->getPoissonSolver(); + Potentials& pot = mgmol->getHamiltonian()->potential(); + std::shared_ptr> rho = mgmol->getRho(); + const int dim = pot.size(); + + /* GridFunc initialization inputs */ + const pb::Grid &grid(poisson->vh().grid()); + short bc[3]; + for (int d = 0; d < 3; d++) + bc[d] = poisson->vh().bc(d); + /* Load Hartree potential basis matrix */ std::string basis_file = rom_options.basis_file; const int num_pot_basis = rom_options.num_potbasis; @@ -272,6 +307,26 @@ void runPoissonROM(MGmolInterface *mgmol_) CAROM::Matrix pot_rom_inv(num_pot_basis, num_pot_basis, false); CAROM::Matrix pot_rhs_rom(num_pot_basis, num_pot_basis, false); CAROM::Vector pot_rhs_rescaler(num_pot_basis, false); + + int nprocs0 = -1, hr_idx_size = -1; + if (MPIdata::onpe0) + { + std::string rom_oper = rom_options.pot_rom_file; + CAROM::HDFDatabase h5_helper; + h5_helper.open(rom_oper, "r"); + + h5_helper.getInteger("potential_rhs_nprocs", nprocs0); + h5_helper.getInteger("potential_rhs_hr_idx_size", hr_idx_size); + if (nprocs0 != nprocs) + { + std::cerr << "runPoissonROM error: same number of processors must be used as for buildPoissonROM!\n" << std::endl; + MPI_Abort(MPI_COMM_WORLD, 0); + } + + h5_helper.close(); + } + mmpi.bcastGlobal(&hr_idx_size); + std::vector global_sampled_row(hr_idx_size), sampled_rows_per_proc(nprocs); /* Load ROM operator */ // read the file from PE0 only @@ -291,6 +346,12 @@ void runPoissonROM(MGmolInterface *mgmol_) h5_helper.getDoubleArray("potential_rom_inverse", pot_rom_inv.getData(), num_pot_basis * num_pot_basis, false); + /* load right-hand side hyper-reduction sample index */ + h5_helper.getIntegerArray("potential_rhs_hr_idx", global_sampled_row.data(), + global_sampled_row.size(), false); + h5_helper.getIntegerArray("potential_rhs_hr_idcs_per_proc", sampled_rows_per_proc.data(), + sampled_rows_per_proc.size(), false); + /* load right-hand side hyper-reduction operator */ h5_helper.getDoubleArray("potential_rhs_rom_inverse", pot_rhs_rom.getData(), num_pot_basis * num_pot_basis, false); @@ -301,6 +362,185 @@ void runPoissonROM(MGmolInterface *mgmol_) h5_helper.close(); } + // broadcast over all processes + mmpi.bcastGlobal(pot_rom.getData(), num_pot_basis * num_pot_basis, 0); + mmpi.bcastGlobal(pot_rom_inv.getData(), num_pot_basis * num_pot_basis, 0); + mmpi.bcastGlobal(pot_rhs_rom.getData(), num_pot_basis * num_pot_basis, 0); + mmpi.bcastGlobal(pot_rhs_rescaler.getData(), num_pot_basis, 0); + mmpi.bcastGlobal(global_sampled_row.data(), hr_idx_size, 0); + mmpi.bcastGlobal(sampled_rows_per_proc.data(), nprocs, 0); + + /* get local sampled row */ + std::vector offsets, sampled_row(sampled_rows_per_proc[rank]); + int num_global_sample = CAROM::get_global_offsets(sampled_rows_per_proc[rank], offsets); + for (int s = 0, gs = offsets[rank]; gs < offsets[rank+1]; gs++, s++) + sampled_row[s] = global_sampled_row[gs]; + + /* ROM currently support only nspin=1 */ + CAROM_VERIFY(rho->rho_.size() == 1); + + /* load the test restart file */ + mgmol->loadRestartFile(rom_options.test_restart_file); + + /* get mgmol orbitals and copy to librom */ + OrbitalsType *orbitals = mgmol->getOrbitals(); + const int chrom_num = orbitals->chromatic_number(); + CAROM::Matrix psi(dim, chrom_num, true); + for (int c = 0; c < chrom_num; c++) + { + ORBDTYPE *d_psi = orbitals->getPsi(c); + for (int d = 0; d < dim ; d++) + psi.item(d, c) = *(d_psi + d); + } + + /* + get rom coefficients of orbitals based on orbital POD basis. + At this point, we take the FOM orbital as it is, + thus rom coefficients become the identity matrix. + */ + CAROM::Matrix rom_psi(chrom_num, chrom_num, false); + for (int i = 0; i < chrom_num; i++) + for (int j = 0; j < chrom_num; j++) + rom_psi(i, j) = (i == j) ? 1 : 0; + + /* get mgmol density matrix */ + ProjectedMatricesInterface *proj_matrices = orbitals->getProjMatrices(); + proj_matrices->updateSubMatX(); + SquareLocalMatrices& localX(proj_matrices->getLocalX()); + + bool dm_distributed = (localX.nmat() > 1); + CAROM_VERIFY(!dm_distributed); + + /* copy density matrix into librom */ + CAROM::Matrix dm(localX.getRawPtr(), localX.m(), localX.n(), dm_distributed, true); + + /* compute ROM rho using hyper reduction */ + CAROM::Vector sample_rho(1, true); // this will be resized in computeRhoOnSamplePts + computeRhoOnSamplePts(dm, psi, rom_psi, sampled_row, sample_rho); + + /* check sampled rho */ + for (int s = 0; s < sampled_row.size(); s++) + { + printf("rank %d, rho[%d]: %.5e, sample_rho: %.5e\n", + rank, sampled_row[s], rho->rho_[0][sampled_row[s]], sample_rho(s)); + } + + sample_rho.gather(); + CAROM::Vector *rom_rho = pot_rhs_rom.mult(sample_rho); + if (rank == 0) + { + printf("rom rho before projection\n"); + for (int d = 0; d < num_pot_basis; d++) + printf("%.5e\t", rom_rho->item(d)); + printf("\n"); + + printf("pot_rhs_rescaler\n"); + for (int d = 0; d < num_pot_basis; d++) + printf("%.5e\t", pot_rhs_rescaler.item(d)); + printf("\n"); + } + + /* rescale the electron density */ + const double nel = ct.getNel(); + // volume element is already multiplied in pot_rhs_rescaler. + const double tcharge = rom_rho->inner_product(pot_rhs_rescaler); + *rom_rho *= nel / tcharge; + if (rank == 0) + printf("rank %d, rho scaler: %.3e\n", rank, nel / tcharge); + + /* check rho projection */ + CAROM::Vector fom_rho_vec(&rho->rho_[0][0], dim, true, false); + CAROM::Vector *rho_proj = pot_basis->transposeMult(fom_rho_vec); + CAROM::Vector *rho_proj2 = pot_basis->mult(*rho_proj); + (*rho_proj2) -= fom_rho_vec; + double rho_proj_error = rho_proj2->norm() / fom_rho_vec.norm(); + if (rank == 0) + { + printf("rom rho\n"); + for (int d = 0; d < num_pot_basis; d++) + printf("%.5e\t", rom_rho->item(d)); + printf("\n"); + printf("fom rho projection\n"); + for (int d = 0; d < num_pot_basis; d++) + printf("%.5e\t", rho_proj->item(d)); + printf("\n"); + + printf("rho proj error: %.5e\n", rho_proj_error); + } + + /* compute ROM ion density using hyper reduction */ + std::shared_ptr ions = mgmol->getIons(); + CAROM::Vector sampled_rhoc(1, true); // this will be resized in evalIonDensityOnSamplePts + pot.evalIonDensityOnSamplePts(*ions, sampled_row, sampled_rhoc); + + /* check sampled rhoc */ + RHODTYPE *rho_comp = pot.rho_comp(); + for (int s = 0; s < sampled_row.size(); s++) + { + printf("rank %d, rhoc[%d]: %.5e, sampled_rhoc: %.5e\n", + rank, sampled_row[s], rho_comp[sampled_row[s]], sampled_rhoc(s)); + } + + sampled_rhoc.gather(); + CAROM::Vector *rom_rhoc = pot_rhs_rom.mult(sampled_rhoc); + + /* rescale the ion density */ + const double ionic_charge = pot.getIonicCharge(); + // volume element is already multiplied in pot_rhs_rescaler. + const double comp_rho = rom_rhoc->inner_product(pot_rhs_rescaler); + *rom_rhoc *= ionic_charge / comp_rho; + if (rank == 0) + printf("rank %d, rhoc scaler: %.3e\n", rank, ionic_charge / comp_rho); + + /* right-hand side */ + *rom_rho -= (*rom_rhoc); + *rom_rho *= (4.0 * M_PI); + + /* solve Poisson ROM */ + CAROM::Vector *rom_pot = pot_rom_inv.mult(*rom_rho); + + /* data array to lift up rom solution */ + std::vector test_sol(dim); + /* get librom view-vector of test_sol[s] */ + CAROM::Vector test_sol_vec(test_sol.data(), dim, true, false); + pot_basis->mult(*rom_pot, test_sol_vec); + + /* mgmol grid function for lifted-up fom solution */ + pb::GridFunc testsol_gf(grid, bc[0], bc[1], bc[2]); + pb::GridFunc fomsol_gf(grid, bc[0], bc[1], bc[2]); + testsol_gf.assign(test_sol.data(), 'd'); + // POTDTYPE *vh_rho = pot.vh_rho(); + // for (int d = 0; d < dim; d++) + // (vh_rho[d]) += 1.0e-3; + fomsol_gf.assign(pot.vh_rho(), 'd'); + + testsol_gf -= fomsol_gf; + double rel_error = testsol_gf.norm2() / fomsol_gf.norm2(); + if (rank == 0) + printf("relative error: %.3e\n", rel_error); + + /* librom view vector for fom solution */ + CAROM::Vector fom_sol_vec(pot.vh_rho(), dim, true, false); + CAROM::Vector *fom_proj = pot_basis->transposeMult(fom_sol_vec); + if (rank == 0) + { + printf("rom vector\n"); + for (int d = 0; d < num_pot_basis; d++) + printf("%.5e\t", rom_pot->item(d)); + printf("\n"); + printf("fom projection\n"); + for (int d = 0; d < num_pot_basis; d++) + printf("%.5e\t", fom_proj->item(d)); + printf("\n"); + } + + /* clean up */ + delete rom_rho; + delete rom_rhoc; + delete rom_pot; + delete fom_proj; + delete rho_proj; + delete rho_proj2; } /* test routines */ @@ -573,10 +813,16 @@ void testROMRhoOperator(MGmolInterface *mgmol_) MGmol *mgmol = static_cast *>(mgmol_); Poisson *poisson = mgmol->electrostat_->getPoissonSolver(); Potentials& pot = mgmol->getHamiltonian()->potential(); - std::shared_ptr> rho = NULL; // mgmol->getRho(); + std::shared_ptr> rho = mgmol->getRho(); const OrthoType ortho_type = rho->getOrthoType(); assert(ortho_type == OrthoType::Nonorthogonal); + /* GridFunc initialization inputs */ + const pb::Grid &grid(poisson->vh().grid()); + short bc[3]; + for (int d = 0; d < 3; d++) + bc[d] = poisson->vh().bc(d); + /* potential should have the same size as rho */ const int dim = pot.size(); @@ -598,11 +844,24 @@ void testROMRhoOperator(MGmolInterface *mgmol_) /* Collect the restart files */ std::string filename; + pb::GridFunc rhogf(grid, bc[0], bc[1], bc[2]); + std::vector rho_outvec(rho->rho_[0].size()); for (int k = minidx; k <= maxidx; k++) { filename = string_format(rom_options.restart_file_fmt, k); mgmol->loadRestartFile(filename); basis_generator.takeSample(&rho->rho_[0][0]); + + rhogf.assign(&rho->rho_[0][0]); + rhogf.init_vect(rho_outvec.data(), 'd'); + for (int d = 0; d < rho_outvec.size(); d++) + { + const double error = abs(rho->rho_[0][d] - rho_outvec[d]); + if (error > 0.0) + printf("rank %d, rho[%d]: %.15e, sample_rho: %.15e\n", + rank, d, rho->rho_[0][d], rho_outvec[d]); + CAROM_VERIFY(error == 0.0); + } } // basis_generator.writeSnapshot(); const CAROM::Matrix rho_snapshots(*basis_generator.getSnapshotMatrix()); @@ -919,13 +1178,13 @@ void testROMIonDensity(MGmolInterface *mgmol_) CAROM_VERIFY(abs(fom_overlap_ions[test_idx][k][d] - ions->overlappingVL_ions()[k]->position(d)) < 1.0e-12); /* eval ion density on sample grid points */ - std::vector sampled_rhoc(sampled_row.size()); + CAROM::Vector sampled_rhoc(1, true); // this will be resized in evalIonDensityOnSamplePts pot.evalIonDensityOnSamplePts(*ions, sampled_row, sampled_rhoc); for (int d = 0; d < sampled_row.size(); d++) { - printf("rank %d, fom rhoc[%d]: %.3e, rom rhoc: %.3e\n", rank, sampled_row[d], fom_rhoc[test_idx][sampled_row[d]], sampled_rhoc[d]); - CAROM_VERIFY(abs(fom_rhoc[test_idx][sampled_row[d]] - sampled_rhoc[d]) < 1.0e-12); + printf("rank %d, fom rhoc[%d]: %.3e, rom rhoc: %.3e\n", rank, sampled_row[d], fom_rhoc[test_idx][sampled_row[d]], sampled_rhoc(d)); + CAROM_VERIFY(abs(fom_rhoc[test_idx][sampled_row[d]] - sampled_rhoc(d)) < 1.0e-12); } } diff --git a/src/rom_workflows.h b/src/rom_workflows.h index 9222249f..313f6f59 100644 --- a/src/rom_workflows.h +++ b/src/rom_workflows.h @@ -36,6 +36,7 @@ namespace po = boost::program_options; #include "librom.h" +#include "hyperreduction/Hyperreduction.h" #include "utils/HDFDatabase.h" #include "utils/mpi_utils.h"