Skip to content

Commit dd15c96

Browse files
committed
Cleanup UpdatePROM
- Don't fix max basis size by using vector reserve - De-duplicate code - Fix PROM printing bug
1 parent f77a82f commit dd15c96

File tree

2 files changed

+67
-58
lines changed

2 files changed

+67
-58
lines changed

palace/models/romoperator.cpp

Lines changed: 51 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -340,17 +340,14 @@ RomOperator::RomOperator(const IoData &iodata, SpaceOperator &space_op,
340340
ksp = std::make_unique<ComplexKspSolver>(iodata, space_op.GetNDSpaces(),
341341
&space_op.GetH1Spaces());
342342

343-
auto excitation_helper = space_op.GetPortExcitations();
343+
MFEM_VERIFY(max_size_per_excitation > 0, "Reduced order basis must have > 0 size!");
344344

345-
// The initial PROM basis is empty. The provided maximum dimension is the number of sample
346-
// points (2 basis vectors per point). Basis orthogonalization method is configured using
347-
// GMRES/FGMRES settings.
348-
MFEM_VERIFY(max_size_per_excitation * excitation_helper.Size() > 0,
349-
"Reduced order basis storage must have > 0 columns!");
350-
V.resize(2 * max_size_per_excitation * excitation_helper.Size(), Vector());
345+
// Reserve empty vectors but don't pre-allocate actual memory size due to overhead.
346+
V.reserve(max_prom_size);
347+
v_node_label.reserve(max_prom_size);
351348

352-
// Set up MRI.
353-
for (const auto &[excitation_idx, data] : excitation_helper)
349+
// Set up MinimalRationalInterpolation.
350+
for (const auto &[excitation_idx, data] : space_op.GetPortExcitations())
354351
{
355352
mri.emplace(excitation_idx, MinimalRationalInterpolation(max_size_per_excitation));
356353
}
@@ -376,6 +373,7 @@ void RomOperator::SetExcitationIndex(int excitation_idx)
376373
else
377374
{
378375
// Project RHS1 to RHS1r with current PROM.
376+
auto dim_V = V.size();
379377
if (dim_V > 0)
380378
{
381379
MPI_Comm comm = space_op.GetComm();
@@ -421,63 +419,58 @@ void RomOperator::SolveHDM(int excitation_idx, double omega, ComplexVector &u)
421419

422420
void RomOperator::UpdatePROM(const ComplexVector &u, std::string_view node_label)
423421
{
424-
425-
// Update V. The basis is always real (each complex solution adds two basis vectors if it
426-
// has a nonzero real and imaginary parts).
422+
// Update PROM basis V. The basis is always real (each complex solution adds two basis
423+
// vectors, if it has a nonzero real and imaginary parts).
427424
BlockTimer bt(Timer::CONSTRUCT_PROM);
428425
MPI_Comm comm = space_op.GetComm();
429426

430-
const double normr = linalg::Norml2(comm, u.Real());
431-
const double normi = linalg::Norml2(comm, u.Imag());
432-
const bool has_real = (normr > ORTHOG_TOL * std::sqrt(normr * normr + normi * normi));
433-
const bool has_imag = (normi > ORTHOG_TOL * std::sqrt(normr * normr + normi * normi));
427+
const auto norm_re = linalg::Norml2(comm, u.Real());
428+
const auto norm_im = linalg::Norml2(comm, u.Imag());
429+
const auto norm_tol = ORTHOG_TOL * std::sqrt(norm_re * norm_re + norm_im * norm_im);
430+
const bool has_real = (norm_re > norm_tol);
431+
const bool has_imag = (norm_im > norm_tol);
432+
433+
const std::size_t dim_V_old = V.size();
434+
const std::size_t dim_V_new =
435+
V.size() + static_cast<std::size_t>(has_real) + static_cast<std::size_t>(has_imag);
434436

435-
const std::size_t dim_V_new = dim_V + std::size_t(has_real) + std::size_t(has_imag);
436-
MFEM_VERIFY(dim_V_new <= V.size(),
437-
"Unable to increase basis storage size, increase maximum number of vectors!");
438-
const std::size_t dim_V_old = dim_V;
439-
v_node_label.reserve(dim_V_new);
440437
orth_R.conservativeResizeLike(Eigen::MatrixXd::Zero(dim_V_new, dim_V_new));
441438

439+
auto add_real_vector_to_basis = [this](const Vector &vector, std::string_view full_label)
440+
{
441+
auto dim_V = V.size();
442+
V.push_back(vector);
443+
OrthogonalizeColumn(orthog_type, space_op.GetComm(), V, V.back(),
444+
orth_R.col(dim_V).data(), dim_V);
445+
orth_R(dim_V, dim_V) = linalg::Norml2(space_op.GetComm(), V.back());
446+
V.back() *= 1.0 / orth_R(dim_V, dim_V);
447+
v_node_label.emplace_back(full_label);
448+
};
449+
442450
if (has_real)
443451
{
444-
V[dim_V] = u.Real();
445-
OrthogonalizeColumn(orthog_type, comm, V, V[dim_V], orth_R.col(dim_V).data(), dim_V);
446-
orth_R(dim_V, dim_V) = linalg::Norml2(comm, V[dim_V]);
447-
V[dim_V] *= 1.0 / orth_R(dim_V, dim_V);
448-
v_node_label.emplace_back(fmt::format("{}_re", node_label));
449-
dim_V++;
452+
add_real_vector_to_basis(u.Real(), fmt::format("{}_re", node_label));
450453
}
451454
if (has_imag)
452455
{
453-
V[dim_V] = u.Imag();
454-
OrthogonalizeColumn(orthog_type, comm, V, V[dim_V], orth_R.col(dim_V).data(), dim_V);
455-
orth_R(dim_V, dim_V) = linalg::Norml2(comm, V[dim_V]);
456-
V[dim_V] *= 1.0 / orth_R(dim_V, dim_V);
457-
v_node_label.emplace_back(fmt::format("{}_im", node_label));
458-
dim_V++;
456+
add_real_vector_to_basis(u.Imag(), fmt::format("{}_im", node_label));
459457
}
460458

461-
// Update reduced-order operators. Resize preserves the upper dim0 x dim0 block of each
462-
// matrix and first dim0 entries of each vector and the projection uses the values
463-
// computed for the unchanged basis vectors.
464-
Kr.conservativeResize(dim_V, dim_V);
459+
// Update reduced-order operators.
460+
Kr.conservativeResize(dim_V_new, dim_V_new);
465461
ProjectMatInternal(comm, V, *K, Kr, r, dim_V_old);
466462
if (C)
467463
{
468-
Cr.conservativeResize(dim_V, dim_V);
464+
Cr.conservativeResize(dim_V_new, dim_V_new);
469465
ProjectMatInternal(comm, V, *C, Cr, r, dim_V_old);
470466
}
471-
Mr.conservativeResize(dim_V, dim_V);
467+
Mr.conservativeResize(dim_V_new, dim_V_new);
472468
ProjectMatInternal(comm, V, *M, Mr, r, dim_V_old);
473469
if (RHS1.Size())
474470
{
475-
RHS1r.conservativeResize(dim_V);
471+
RHS1r.conservativeResize(dim_V_new);
476472
ProjectVecInternal(comm, V, RHS1, RHS1r, dim_V_old);
477473
}
478-
// Placeholder matrices.
479-
Ar.resize(dim_V, dim_V);
480-
RHSr.resize(dim_V);
481474
}
482475

483476
void RomOperator::UpdateMRI(int excitation_idx, double omega, const ComplexVector &u)
@@ -494,7 +487,18 @@ void RomOperator::SolvePROM(int excitation_idx, double omega, ComplexVector &u)
494487
// the matrix Aᵣ(ω) = Kᵣ + iω Cᵣ - ω² Mᵣ + Vᴴ A2 V(ω) and source vector RHSᵣ(ω) =
495488
// iω RHS1ᵣ + Vᴴ RHS2(ω). A2(ω) and RHS2(ω) are constructed only if required and are
496489
// only nonzero on boundaries, will be empty if not needed.
497-
if (has_A2 && Ar.rows() > 0)
490+
491+
// No basis states ill-defined: return zero vector to match current behaviour.
492+
if (V.size() == 0)
493+
{
494+
u = 0.0;
495+
return;
496+
}
497+
498+
Ar.resize(V.size(), V.size());
499+
RHSr.resize(V.size());
500+
501+
if (has_A2)
498502
{
499503
A2 = space_op.GetExtraSystemMatrix<ComplexOperator>(omega, Operator::DIAG_ZERO);
500504
ProjectMatInternal(space_op.GetComm(), V, *A2, Ar, r, 0);
@@ -510,7 +514,7 @@ void RomOperator::SolvePROM(int excitation_idx, double omega, ComplexVector &u)
510514
}
511515
Ar += (-omega * omega) * Mr;
512516

513-
if (has_RHS2 && RHSr.size() > 0)
517+
if (has_RHS2)
514518
{
515519
space_op.GetExcitationVector2(excitation_idx, omega, RHS2);
516520
ProjectVecInternal(space_op.GetComm(), V, RHS2, RHSr, 0);
@@ -587,7 +591,7 @@ void RomOperator::PrintPROMMatrices(const Units &units, const fs::path &post_dir
587591
auto unit_farad = units.GetScaleFactor<Units::ValueType::CAPACITANCE>();
588592
auto m_C = (unit_farad * v_d) * Mr * v_d;
589593
print_table(m_C.real(), "rom-C-re.csv");
590-
if (C->Imag())
594+
if (M->Imag())
591595
{
592596
print_table(m_C.imag(), "rom-C-im.csv");
593597
}
@@ -599,7 +603,7 @@ void RomOperator::PrintPROMMatrices(const Units &units, const fs::path &post_dir
599603
auto unit_ohm = units.GetScaleFactor<Units::ValueType::IMPEDANCE>();
600604
auto m_Rinv = ((1.0 / unit_ohm) * v_d) * Cr * v_d;
601605
print_table(m_Rinv.real(), "rom-Rinv-re.csv");
602-
if (M->Imag())
606+
if (C->Imag())
603607
{
604608
print_table(m_Rinv.imag(), "rom-Rinv-im.csv");
605609
}

palace/models/romoperator.hpp

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ class RomOperator
5353
{
5454
private:
5555
// Reference to HDM discretization (not owned).
56+
// TODO(C++20): Use std::reference_wrapper with incomplete types.
5657
SpaceOperator &space_op;
5758

5859
// Used for constructing & resuse of RHS1.
@@ -76,20 +77,24 @@ class RomOperator
7677
std::unique_ptr<ComplexKspSolver> ksp;
7778

7879
// PROM matrices and vectors. Projected matrices are Mr = Vᴴ M V where V is the reduced
79-
// order basis defined below. Frequency dependant matrix Ar and RHSr are assembled and
80-
// used only during SolvePROM.
80+
// order basis defined below.
8181
Eigen::MatrixXcd Kr, Mr, Cr; // Extend during UpdatePROM as modes are added
82-
Eigen::VectorXcd RHS1r; // Extend but need to recompute on excitation change
83-
Eigen::MatrixXcd Ar; // Pre-allocation only: assembled and used in SolvePROM
84-
Eigen::VectorXcd RHSr; // Pre-allocation only: assembled and used in SolvePROM
82+
Eigen::VectorXcd RHS1r; // Need to recompute drive vector on excitation change.
8583

86-
// PROM reduced-order basis (real-valued) and active dimension.
84+
// Frequency dependant PROM matrix Ar and RHSr are assembled and used only during
85+
// SolvePROM. Define them here so memory allocation is reused in "online" evaluation.
86+
Eigen::MatrixXcd Ar;
87+
Eigen::VectorXcd RHSr;
88+
89+
// PROM reduced-order basis (real-valued).
8790
std::vector<Vector> V;
88-
std::size_t dim_V = 0;
8991
Orthogonalization orthog_type;
90-
std::vector<std::string> v_node_label; // Label to distinguish port modes from solution
91-
// projection and to print PROM matrices
92-
Eigen::MatrixXd orth_R; // Upper-triangular R; U = VR with U modes
92+
93+
// Label to distinguish port modes from solution projection and to print PROM matrices
94+
std::vector<std::string> v_node_label;
95+
96+
// Upper-triangular orthognoalization matrix R; U = VR with U modes. Memory pre-allocated.
97+
Eigen::MatrixXd orth_R;
9398

9499
// MRIs: one for each excitation index. Only used to pick new frequency sample point.
95100
std::map<int, MinimalRationalInterpolation> mri;
@@ -102,7 +107,7 @@ class RomOperator
102107
const ComplexKspSolver &GetLinearSolver() const { return *ksp; }
103108

104109
// Return PROM dimension.
105-
auto GetReducedDimension() const { return dim_V; }
110+
auto GetReducedDimension() const { return V.size(); }
106111

107112
// Return set of sampled parameter points for basis construction.
108113
const auto &GetSamplePoints(int excitation_idx) const

0 commit comments

Comments
 (0)