Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor measurement and printing for future features #302

Open
wants to merge 15 commits into
base: main
Choose a base branch
from

Conversation

phdum-a
Copy link
Collaborator

@phdum-a phdum-a commented Nov 19, 2024

Refactoring of measurement and printing system in preparation for multi-excitation support.

  • Separates outs measurements (to be done in PostOperator only) from printing (to be done by Solvers)
  • Printing is now change from Postpro functions into small printer classes which also hold a table of results, rather than printing row-by-row. (This is where multi-excitations will add extra columns).
  • The output path is now handled using std::filesystem more consistently
  • Measurements in PostOperator are now cached so that printers don't have to worry about that the getter functions might trigger an expensive calculation or one that requires MPI communication.
  • Add PALACE_MFEM_USE_EXCEPTIONS build flag for unit tests, since catch can't test for aborts.
  • Other small tweaks and improvements

Note: The output of all solvers after these changes should be identical to the current output*, for easy testing. Changes which alter output will be in subsequent PRs.
[*With one trivial change in the logger for eigenvalues from \omega/2pi -> f]

@phdum-a phdum-a requested a review from hughcars November 19, 2024 23:48
@phdum-a phdum-a force-pushed the phdum/new_postpro branch 4 times, most recently from 2e0b7f0 to c1a8a4b Compare November 22, 2024 23:48
- export build commands
- Used for testing since Catch does not support testing aborts
- Add tests
- Add table wrapper for csv files for open-write-close
- Make empty cell NULL
- switch post-processing functions to small classes that store data in tables
- replace sequential row printing
- separate write during sweep step and final write more clearly
- Requires non-empty output dir
- Canonicalize and sync among processors
- Introduces palace::fs namespace to stop injecting into std
- Simplify checks since output directory always exists
- Cleaner separation between measurement (PostOperator) and printing (in solvers)
- Treat frequency as measurement to be supplied by solver
- Add caching system in PostOperator for all measurements, extending port case
- Simplify energy measurements using caching system
- Store pointer view to lumped/wave port operators in PostOperator where appropriate
- remove redunant default init
- init doubles in postop
@fedeinthemix
Copy link

Hi, since you are refactoring the output format to support multi-excitations, I'd like to ask if would consider adding an option to output S-parameter files in Touchstone format. It's a de-facto standard in circuit simulators and beyond.

@phdum-a
Copy link
Collaborator Author

phdum-a commented Dec 10, 2024

Hi, since you are refactoring the output format to support multi-excitations, I'd like to ask if would consider adding an option to output S-parameter files in Touchstone format. It's a de-facto standard in circuit simulators and beyond.

Dear @fedeinthemix,

Thank you very much for your suggestion. We'll have to think about it more, but right now we don't have plans to print touchstone files directly in Palace.

In cases where people require touchstone files, we have recommended writing a script that reads in the palace csv files from all the separate simulations (e.g. using pandas) and then prints it in the right form. You could also look at scikit-rf that has a touchstone printer and parser.

For Palace, we do want a consistant file formats with other measurements not covered by touchstone, like energy or fields. Also the touchstone spec does not quite cover some cases, like simulating only a subset of columns of the s-matrix.

Please let use know, if there is there anything you think we are missing something in the Palace output. For example, is there something in the touchstone file format / metadata that you can't find easily?

Thanks!

@fedeinthemix
Copy link

Dear @phdum-a,

Thank you for your answer. I thought that an option to output Touchstone files would be convenient since most RF/mmW passive circuits simulated with palace will probably be used in circuit simulators. But, I understand point, and, as you say, writing a conversion procedure is not a big deal.

Thanks again for your answer.

Copy link
Collaborator

@hughcars hughcars left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally looks pretty good. I have some changes I think need to be made at #325 from hughcars/new_postpro. Additionally given it's a large refactor, mention it in the CHANGELOG.md.

@@ -76,6 +76,9 @@ if(CMAKE_BUILD_TYPE MATCHES "Debug|debug|DEBUG")
endif()
endif()

# Replace mfem abort calls with exceptions for testing, default off
set(PALACE_MFEM_USE_EXCEPTIONS CACHE NO "MFEM throw exceptsions instead of abort calls")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

set(PALACE_MFEM_USE_EXCEPTIONS CACHE NO BOOL "MFEM throw exceptsions instead of abort calls")
The BOOL is needed to calm a warning about using the cache. However if you do that, the mfem build then fails due to their parsing of the variable, giving

set(MFEM_DEBUG OFF)
set(MFEM_USE_EXCEPTIONS CACHE)
set(MFEM_USE_ZLIB YES)

in the mfem cmake cache. I think sticking with
set(PALACE_MFEM_USE_EXCEPTIONS NO) rather than needing to work around mfem's cmake approach.

buf.append(buf_a);
}

Mpi::Print("{}", fmt::to_string(buf));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This buffering trick will print everything in one shot rather than scrolling. I don't think its an issue as this goes by so fast anyway as to be indistinguishable from a block print. Only issue would be if anything in here can error and thereby print nothing.

Given there's a few of these buffer printing options, you might consider having an Mpi::Print overload that ingests just a buffer, Mpi::Print(fmt::memory_buffer); that does this under the hood if it's going to be very common. Especially given these buffers are being sent in with no formatting, just directly as strings.

// View to default options in table class, will be set when columns are added to Table
ColumnOptions *defaults = nullptr;

// ----
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unneeded, delete.

{
friend class Table;

// View to default options in table class, will be set when columns are added to Table
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Any comment on a new line ends with .,

// New line comment.
int v = 0; // Can end without

class TableWithCSVFile
{
std::string csv_file_fullpath_ = "";
unsigned long file_append_curser = -1;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment describing what this variable is used for.

// Common domain postprocessing for all simulation types.
void PostprocessDomains(const PostOperator &post_op, const std::string &name, int step,
double time, double E_elec, double E_mag, double E_cap,
double E_ind) const;

// Common surface postprocessing for all simulation types.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expand on this documentation, the disctinction between a printer and the performing of the actual measurement is one of the biggest innovations in this PR. A short sentence or two here describing this distinction would be good. Can then reference this in doc string above the printers in the other driver classes.

Comment on lines +247 to +248
// -----------------
// Measurements / Postprocessing
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delete.

Comment on lines +59 to +61
// Port Contributions: not owned, view onto space_op only, it must not go out of scope
LumpedPortOperator *lumped_port_op = nullptr;
WavePortOperator *wave_port_op = nullptr;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do not want these classes referenced within post operator, which shouldn't know about them, other than via a higher level operator (SpaceOperator etc.). I have a refactor that removes the need for this, by passing them in at the relevant call sites, and doing away with the use of optionals/lazy evaluation of the measurement cache. See hughcars/new_postpro

Comment on lines +94 to +115
struct MeasurementCache
{
std::optional<std::complex<double>> omega = std::nullopt;

std::optional<double> domain_E_field_energy_all = std::nullopt;
std::optional<double> domain_H_field_energy_all = std::nullopt;

std::optional<std::map<int, double>> domain_E_field_energy_i = std::nullopt;
std::optional<std::map<int, double>> domain_H_field_energy_i = std::nullopt;

std::optional<std::vector<FluxData>> surface_flux_i = std::nullopt;
std::optional<std::vector<InterfaceData>> interface_eps_i = std::nullopt;

std::optional<std::map<int, PortPostData>> lumped_port_vi = std::nullopt;
std::optional<std::map<int, PortPostData>> wave_port_vi = std::nullopt;

std::optional<double> lumped_port_inductor_energy = std::nullopt;
std::optional<double> lumped_port_capacitor_energy = std::nullopt;

std::optional<std::vector<std::complex<double>>> probe_E_field = std::nullopt;
std::optional<std::vector<std::complex<double>>> probe_B_field = std::nullopt;
};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not need optionals on here, many of the measurements are setting the non-evaluable versions to 0.0, and the containers all ready have a notion of size. Removing the optional, and moving all the measurements to occur within the MeasureAll method, can also use concrete values whilst removing the need for mutability aswell. See hughcars/new_postpro

Also, measurement_cache.

Comment on lines +365 to +367
void PostOperator::MeasureAll()
{
if (A)
ClearAllMeasurementCache();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be simplified significantly by using the fact we know when MeasureAll will be called will be the only time to do any measurements. The lazy evaluation isn't necessary. See hughcars/new_postpro

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants