Skip to content

Commit f2b6e19

Browse files
committed
Small fixes; added comments; update changelog
1 parent 3b5fc11 commit f2b6e19

File tree

9 files changed

+195
-144
lines changed

9 files changed

+195
-144
lines changed

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,20 @@ The format of this changelog is based on
5757
lumped port discovery to fail by reducing default from `1e-3` to `1e-12`.
5858
- Fixed bug in Nastran mesh reader where carriage returns (`\r`) in the mesh file could
5959
cause a failure to read the mesh.
60+
- Changed post-processing, so that appropriate measurements are always written to disk as CSV
61+
files. This is a breaking change, since users are now required to specify valid a output
62+
folder in `config["Problem"]["Output"]`. Previously an empty string `""` would suppress file
63+
printing. ParaView printing is still controlled and suppressed by the `"Save"` or `"SaveStep"`
64+
options in the `config["Solver"][...]`.
65+
- Changed measurement and printing during post-processing substantially. This change is not
66+
user-facing, but will enable future multi-excitation support. All measurements are now
67+
performed as part of the `PostOperator` class, which is templated on solver type. The public
68+
interface of the `PostOperator` has been simplified: measurements can no longer be called
69+
individually, but are grouped in a `MeasurePrintAll` function. The actual printing of
70+
measurements has moved out of individual solvers and is orchestrated by the `PostOperatorCSV`
71+
class. Other helper classes include a small `Table` class (for data storage and formatting)
72+
and a light wrapper `TableWithCSVFile` (with file interaction).
73+
- Changed unit conversion interface: this was moved out of `IOData` into a separate `Units` class.
6074

6175
## [0.13.0] - 2024-05-20
6276

palace/drivers/drivensolver.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ DrivenSolver::SweepAdaptive(SpaceOperator &space_op,
291291
// B = -1/(iω) ∇ x E + 1/ω kp x E
292292
floquet_corr->AddMult(E, B, 1.0 / omega);
293293
}
294-
// Measure domain energies for the error indictor only: don't exchange face_nbr_data.
294+
// Measure domain energies for the error indicator only: don't exchange face_nbr_data.
295295
auto total_domain_energy = post_op.MeasureDomainFieldEnergyOnly(E, B, false);
296296
estimator.AddErrorIndicator(E, B, total_domain_energy, indicator);
297297
};
@@ -384,8 +384,7 @@ DrivenSolver::SweepAdaptive(SpaceOperator &space_op,
384384
// B = -1/(iω) ∇ x E + 1/ω kp x E
385385
floquet_corr->AddMult(E, B, 1.0 / omega);
386386
}
387-
388-
auto measurement = post_op.MeasurePrintAll(step, E, B, omega);
387+
post_op.MeasurePrintAll(step, E, B, omega);
389388
}
390389
// Final postprocessing & printing
391390
BlockTimer bt0(Timer::POSTPRO);

palace/fem/interpolator.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ constexpr auto GSLIB_NEWTON_TOL = 1.0e-12;
2222

2323
namespace
2424
{
25+
26+
// Statically determine vector sizes associated with FiniteElementSpace before individual
27+
// elements are constructed. Remove once mfem 4.8 is release. See
28+
// https://github.com/mfem/mfem/pull/4598.
29+
//
30+
2531
int MFEM_GridFunction_VectorDim(const mfem::FiniteElementSpace *fes)
2632
{
2733
using namespace mfem;

palace/models/postoperator.cpp

Lines changed: 21 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -42,34 +42,6 @@ std::string ParaviewFoldername(const config::ProblemData::Type solver_t)
4242
}
4343
}
4444

45-
// bool WriteParaviewFields(const config::ProblemData::Type solver_t, const IoData &iodata)
46-
// {
47-
// if (solver_t == config::ProblemData::Type::DRIVEN)
48-
// {
49-
// return (iodata.solver.driven.delta_post > 0);
50-
// }
51-
// else if (solver_t == config::ProblemData::Type::EIGENMODE)
52-
// {
53-
// return (iodata.solver.eigenmode.n_post > 0);
54-
// }
55-
// else if (solver_t == config::ProblemData::Type::ELECTROSTATIC)
56-
// {
57-
// return (iodata.solver.electrostatic.n_post > 0);
58-
// }
59-
// else if (solver_t == config::ProblemData::Type::ELECTROSTATIC)
60-
// {
61-
// return (iodata.solver.magnetostatic.n_post > 0);
62-
// }
63-
// else if (solver_t == config::ProblemData::Type::TRANSIENT)
64-
// {
65-
// return (iodata.solver.transient.delta_post > 0);
66-
// }
67-
// else
68-
// {
69-
// return false;
70-
// }
71-
// }
72-
7345
} // namespace
7446

7547
template <config::ProblemData::Type solver_t>
@@ -78,7 +50,7 @@ PostOperator<solver_t>::PostOperator(const IoData &iodata, fem_op_t<solver_t> &f
7850
: fem_op(&fem_op_), units(iodata.units), post_dir(iodata.problem.output),
7951
post_op_csv(this, nr_expected_measurement_rows),
8052
surf_post_op(iodata, fem_op->GetMaterialOp(), fem_op->GetH1Space()),
81-
// dom_post_op does not have a default ctor so immediately specialize via lambda
53+
// dom_post_op does not have a default ctor so specialize via immediate lambda.
8254
dom_post_op(std::move(
8355
[&iodata, &fem_op_]()
8456
{
@@ -100,7 +72,7 @@ PostOperator<solver_t>::PostOperator(const IoData &iodata, fem_op_t<solver_t> &f
10072
}())),
10173
interp_op(iodata, fem_op->GetNDSpace())
10274
{
103-
// Define primary grid-functions
75+
// Define primary grid-functions.
10476
if constexpr (HasVGridFunction<solver_t>())
10577
{
10678
V = std::make_unique<GridFunction>(fem_op->GetH1Space());
@@ -120,7 +92,7 @@ PostOperator<solver_t>::PostOperator(const IoData &iodata, fem_op_t<solver_t> &f
12092
HasComplexGridFunction<solver_t>());
12193
}
12294

123-
// Add wave port boundary mode postprocessing when available.
95+
// Add wave port boundary mode postprocessing, if available.
12496
if constexpr (std::is_same_v<fem_op_t<solver_t>, SpaceOperator>)
12597
{
12698
for (const auto &[idx, data] : fem_op->GetWavePortOp())
@@ -156,7 +128,7 @@ PostOperator<solver_t>::PostOperator(const IoData &iodata, fem_op_t<solver_t> &f
156128
}
157129
InitializeParaviewDataCollection();
158130

159-
// Initilize CSV files for measurements
131+
// Initilize CSV files for measurements.
160132
post_op_csv.InitializeCSVDataCollection();
161133
}
162134

@@ -414,7 +386,6 @@ void PostOperator<solver_t>::WriteFields(double time, int step)
414386

415387
// Given the electric field and magnetic flux density, write the fields to disk for
416388
// visualization. Write the mesh coordinates in the same units as originally input.
417-
// TODO(phdum): remove conditional now that we have fem_op always?
418389
mfem::ParMesh &mesh = E ? *E->ParFESpace()->GetParMesh() : *B->ParFESpace()->GetParMesh();
419390
mesh::DimensionalizeMesh(mesh, mesh_Lc0);
420391
ScaleGridFunctions(mesh_Lc0, mesh.Dimension(), HasComplexGridFunction<solver_t>(), E, B,
@@ -428,6 +399,7 @@ void PostOperator<solver_t>::WriteFields(double time, int step)
428399
mesh::NondimensionalizeMesh(mesh, mesh_Lc0);
429400
ScaleGridFunctions(1.0 / mesh_Lc0, mesh.Dimension(), HasComplexGridFunction<solver_t>(),
430401
E, B, V, A);
402+
Mpi::Barrier(fem_op->GetComm());
431403
}
432404

433405
template <config::ProblemData::Type solver_t>
@@ -446,7 +418,6 @@ void PostOperator<solver_t>::WriteFieldsFinal(const ErrorIndicator *indicator)
446418
// need for these to be parallel objects, since the data is local to each process and
447419
// there isn't a need to ever access the element neighbors. We set the time to some
448420
// non-used value to make the step identifiable within the data collection.
449-
// TODO(phdum): remove conditional now that we have fem_op always?
450421
mfem::ParMesh &mesh = E ? *E->ParFESpace()->GetParMesh() : *B->ParFESpace()->GetParMesh();
451422
mesh::DimensionalizeMesh(mesh, mesh_Lc0);
452423
paraview->SetCycle(paraview->GetCycle() + 1);
@@ -513,6 +484,7 @@ void PostOperator<solver_t>::WriteFieldsFinal(const ErrorIndicator *indicator)
513484
paraview->RegisterVCoeffField(name, gf);
514485
}
515486
mesh::NondimensionalizeMesh(mesh, mesh_Lc0);
487+
Mpi::Barrier(fem_op->GetComm());
516488
}
517489

518490
// Measurements
@@ -879,20 +851,21 @@ void PostOperator<solver_t>::MeasureSurfaceFlux() const
879851
template <config::ProblemData::Type solver_t>
880852
void PostOperator<solver_t>::MeasureInterfaceEFieldEnergy() const
881853
{
882-
// Depends on Capacitive Lumped Port Energy
854+
// Depends on Lumped Port Energy since this is used in normalization of participation
855+
// ratio.
883856

884857
// Compute the surface dielectric participation ratio and associated quality factor for
885858
// the material interface given by index idx. We have:
886859
// 1/Q_mj = p_mj tan(δ)_j
887860
// with:
888-
// p_mj = 1/2 t_j Re{∫_{Γ_j} (ε_j E_m)ᴴ E_m dS} /(E_elec + E_cap).
861+
// p_mj = 1/2 t_j Re{∫_{Γ_j} (ε_j E_m)ᴴ E_m dS} / (E_elec + E_cap).
889862
measurement_cache.interface_eps_i.clear();
890863
if constexpr (HasEGridFunction<solver_t>())
891864
{
892-
893-
// These two must have been measured first.
894-
// E_cap returns zero if the solver does not support lumped ports.
895-
// TODO: Shold this not include other types of energy too (surface impedance case)?
865+
// Domain and port energies must have been measured first. E_cap returns zero if the
866+
// solver does not support lumped ports.
867+
//
868+
// TODO: Should this not include other types of energy too (surface impedance case)?
896869
auto energy_electric_all = measurement_cache.domain_E_field_energy_all +
897870
measurement_cache.lumped_port_capacitor_energy;
898871

@@ -920,6 +893,7 @@ void PostOperator<solver_t>::MeasureProbes() const
920893
measurement_cache.probe_E_field.clear();
921894
measurement_cache.probe_B_field.clear();
922895

896+
#if defined(MFEM_USE_GSLIB)
923897
if constexpr (HasEGridFunction<solver_t>())
924898
{
925899
if (interp_op.GetProbes().size() > 0)
@@ -936,6 +910,7 @@ void PostOperator<solver_t>::MeasureProbes() const
936910
units.Dimensionalize<Units::ValueType::FIELD_B>(interp_op.ProbeField(*B));
937911
}
938912
}
913+
#endif
939914
}
940915

941916
using fmt::format;
@@ -949,7 +924,6 @@ auto PostOperator<solver_t>::MeasurePrintAll(int step, const ComplexVector &e,
949924
-> std::enable_if_t<U == config::ProblemData::Type::DRIVEN, double>
950925
{
951926
BlockTimer bt0(Timer::POSTPRO);
952-
953927
auto freq = units.Dimensionalize<Units::ValueType::FREQUENCY>(omega);
954928
SetEGridFunction(e);
955929
SetBGridFunction(b);
@@ -963,7 +937,7 @@ auto PostOperator<solver_t>::MeasurePrintAll(int step, const ComplexVector &e,
963937
if (write_paraview_fields() && (step % paraview_delta_post == 0))
964938
{
965939
Mpi::Print("\n");
966-
WriteFields(step / paraview_delta_post, freq_re);
940+
WriteFields(double(step) / paraview_delta_post, freq_re);
967941
Mpi::Print(" Wrote fields to disk at step {:d}\n", step + 1);
968942
}
969943
double total_energy = units.NonDimensionalize<Units::ValueType::ENERGY>(
@@ -981,7 +955,6 @@ auto PostOperator<solver_t>::MeasurePrintAll(int step, const ComplexVector &e,
981955
-> std::enable_if_t<U == config::ProblemData::Type::EIGENMODE, double>
982956
{
983957
BlockTimer bt0(Timer::POSTPRO);
984-
985958
auto freq = units.Dimensionalize<Units::ValueType::FREQUENCY>(omega);
986959
SetEGridFunction(e);
987960
SetBGridFunction(b);
@@ -995,8 +968,8 @@ auto PostOperator<solver_t>::MeasurePrintAll(int step, const ComplexVector &e,
995968
measurement_cache.error_bkwd = error_bkwd;
996969

997970
// Mini pretty-print table of eig summaries: always print with header since other
998-
// measurements may log their results. TODO(phdum+hughcars): Discuss.
999-
if (Mpi::Root(GetComm()))
971+
// measurements may log their results.
972+
if (Mpi::Root(fem_op->GetComm()))
1000973
{
1001974
Table table;
1002975
int idx_pad = 1 + static_cast<int>(std::log10(num_conv));
@@ -1085,7 +1058,6 @@ auto PostOperator<solver_t>::MeasurePrintAll(int step, const Vector &e, const Ve
10851058
-> std::enable_if_t<U == config::ProblemData::Type::TRANSIENT, double>
10861059
{
10871060
BlockTimer bt0(Timer::POSTPRO);
1088-
10891061
auto time = units.Dimensionalize<Units::ValueType::TIME>(t);
10901062
SetEGridFunction(e);
10911063
SetBGridFunction(b);
@@ -1098,7 +1070,7 @@ auto PostOperator<solver_t>::MeasurePrintAll(int step, const Vector &e, const Ve
10981070
if (write_paraview_fields() && (step % paraview_delta_post == 0))
10991071
{
11001072
Mpi::Print("\n");
1101-
WriteFields(step / paraview_delta_post, time);
1073+
WriteFields(double(step) / paraview_delta_post, time);
11021074
Mpi::Print(" Wrote fields to disk at step {:d}\n", step + 1);
11031075
}
11041076
double total_energy = units.NonDimensionalize<Units::ValueType::ENERGY>(
@@ -1144,7 +1116,8 @@ template class PostOperator<config::ProblemData::Type::ELECTROSTATIC>;
11441116
template class PostOperator<config::ProblemData::Type::MAGNETOSTATIC>;
11451117
template class PostOperator<config::ProblemData::Type::TRANSIENT>;
11461118

1147-
// Function explict instation (TODO(C++20): with requires, we won't need a second template)
1119+
// Function explict instantiation.
1120+
// TODO(C++20): with requires, we won't need a second template
11481121

11491122
template auto PostOperator<config::ProblemData::Type::DRIVEN>::MeasurePrintAll<
11501123
config::ProblemData::Type::DRIVEN>(int step, const ComplexVector &e,

0 commit comments

Comments
 (0)