-
Notifications
You must be signed in to change notification settings - Fork 17
Polymorphic injectors, external currents, and diagnostics #377
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
Changes from 4 commits
f62ec7c
496ad5c
7a35b95
a175f13
99beaf1
1be2d52
1c2ddfb
3931fad
0e6f2a5
ddc197a
bc43fb1
358a042
c3fbc68
f4a58a6
7ee609c
dceac22
ad80822
95256de
77fa1a5
119a734
17fe7ab
70c54da
bb1298b
dc5cf86
3c16414
79e444a
9707ee8
88e83fe
02902d0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| #pragma once | ||
|
|
||
| template <typename MPARTICLES, typename MFIELDS_STATE> | ||
| struct InjectorBase | ||
| { | ||
| using Mparticles = MPARTICLES; | ||
| using MfieldsState = MFIELDS_STATE; | ||
|
|
||
| virtual void inject(Mparticles& mprts, MfieldsState& mflds) = 0; | ||
| }; | ||
|
|
||
| template <typename MPARTICLES, typename MFIELDS_STATE> | ||
| struct InjectFromLambda : InjectorBase<MPARTICLES, MFIELDS_STATE> | ||
| { | ||
| using Mparticles = MPARTICLES; | ||
| using MfieldsState = MFIELDS_STATE; | ||
|
|
||
| InjectFromLambda(std::function<void(Mparticles&, MfieldsState&)> lambda) | ||
| : lambda{lambda} | ||
| {} | ||
|
|
||
| void inject(Mparticles& mprts, MfieldsState& mflds) override | ||
| { | ||
| return lambda(mprts, mflds); | ||
| } | ||
|
|
||
| private: | ||
| std::function<void(Mparticles&, MfieldsState&)> lambda; | ||
|
JamesMcClung marked this conversation as resolved.
Outdated
|
||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,6 +8,7 @@ | |
| #include <setup_particles.hxx> | ||
|
|
||
| #include "../libpsc/vpic/fields_item_vpic.hxx" | ||
| #include "injector_base.hxx" | ||
| #include <checks_params.hxx> | ||
| #include <output_particles.hxx> | ||
| #include <push_particles.hxx> | ||
|
|
@@ -343,7 +344,9 @@ struct Psc | |
|
|
||
| // === particle injection | ||
| prof_start(pr_inject_prts); | ||
| inject_particles_(mprts_, mflds_); | ||
| for (auto injector : injectors) { | ||
| injector->inject(mprts_, mflds_); | ||
| } | ||
| prof_stop(pr_inject_prts); | ||
|
|
||
| // === external current | ||
|
|
@@ -470,6 +473,8 @@ private: | |
| public: | ||
| const Grid_t& grid() { return *grid_; } | ||
|
|
||
| std::vector<InjectorBase<Mparticles, MfieldsState>*> injectors; | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I read somewhere that ideally modern C++ shouldn't use explicit pointers at all. The question here is, what guarantees that the objects pointed to don't go away?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I kinda felt like using fancy pointer wrappers would be more performative than actually beneficial, since the psc integrator may as well be a singleton, and an entire case revolves around calling
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually, what would be the best option, if not
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As I'm working on switching to
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess I haven't looked at the various places where this is being used, the ones I saw initially had a The other alternative I can think of is to just keep a reference, ie., punt on the ownership question -- the one thing that's nice about that is that it guarantees non-null, but generally speaking, I think clear ownership rules are preferable. And I can kinda think of test cases where one might want to have to integrators going at the same time for some kind of cross checking, where it'd generally be nice to not assume singleton behavior, though it's kinda far fetched.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
| private: | ||
| double time_start_; | ||
| PscParams p_; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -142,13 +142,13 @@ TEST(BoundaryInjectorTest, Integration1Particle) | |
| DiagEnergies oute{grid.comm(), 0}; | ||
| auto diagnostics = makeDiagnosticsDefault(outf, outp, oute); | ||
|
|
||
| auto inject_particles = | ||
| BoundaryInjector<ParticleGenerator, PscConfig::PushParticles>{ | ||
| ParticleGenerator(1, 1), grid}; | ||
| auto psc = | ||
| makePscIntegrator<PscConfig>(psc_params, grid, mflds, mprts, balance, | ||
| collision, checks, marder, diagnostics); | ||
|
|
||
| auto psc = makePscIntegrator<PscConfig>(psc_params, grid, mflds, mprts, | ||
| balance, collision, checks, marder, | ||
| diagnostics, inject_particles); | ||
| psc.injectors.push_back( | ||
| new BoundaryInjector<ParticleGenerator, typename PscConfig::PushParticles>( | ||
| ParticleGenerator(1, 1), grid)); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess this answers the question about the injectors not going away, essentially because they're being leaked ;) Obviously not a big issue as such, but I think Another thing I don't fully like is that the injectors are added after the fact, which kinda means that the integrator isn't fully set up after being constructed. But I think it's acceptable here, in that an empty list of integrators is a valid state.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. And I would have to agree that, as we currently do, passing N objects to the constructor makes that rather ugly...
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The partially-constructed state of the integrator makes me uncomfortable, too... and I was only planning to make it worse over the next few weeks. This seems like a perfect place to use the builder pattern, but it might be a while before that end state is reached. |
||
|
|
||
| // ---------------------------------------------------------------------- | ||
| // set up initial conditions | ||
|
|
@@ -205,13 +205,13 @@ TEST(BoundaryInjectorTest, IntegrationManyParticles) | |
| DiagEnergies oute{grid.comm(), 0}; | ||
| auto diagnostics = makeDiagnosticsDefault(outf, outp, oute); | ||
|
|
||
| auto inject_particles = | ||
| BoundaryInjector<ParticleGenerator, PscConfig::PushParticles>{ | ||
| ParticleGenerator(-1, 1), grid}; | ||
| auto psc = | ||
| makePscIntegrator<PscConfig>(psc_params, grid, mflds, mprts, balance, | ||
| collision, checks, marder, diagnostics); | ||
|
|
||
| auto psc = makePscIntegrator<PscConfig>(psc_params, grid, mflds, mprts, | ||
| balance, collision, checks, marder, | ||
| diagnostics, inject_particles); | ||
| psc.injectors.push_back( | ||
| new BoundaryInjector<ParticleGenerator, PscConfig::PushParticles>( | ||
| ParticleGenerator(-1, 1), grid)); | ||
|
|
||
| // ---------------------------------------------------------------------- | ||
| // set up initial conditions | ||
|
|
@@ -275,11 +275,12 @@ TEST(BoundaryInjectorTest, IntegrationManySpecies) | |
| BoundaryInjector<ParticleGenerator, PscConfig::PushParticles>{ | ||
| ParticleGenerator(-1, 1), grid}; | ||
|
|
||
| auto inject = make_composite(inject_electrons, inject_ions); | ||
| auto psc = | ||
| makePscIntegrator<PscConfig>(psc_params, grid, mflds, mprts, balance, | ||
| collision, checks, marder, diagnostics); | ||
|
|
||
| auto psc = makePscIntegrator<PscConfig>(psc_params, grid, mflds, mprts, | ||
| balance, collision, checks, marder, | ||
| diagnostics, inject); | ||
| psc.injectors.push_back(&inject_ions); | ||
| psc.injectors.push_back(&inject_electrons); | ||
|
|
||
| // ---------------------------------------------------------------------- | ||
| // set up initial conditions | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I may learn more as I go through more commits, but at this point I'm wondering, why not stick with
operator()?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would say explicit method names helps with discoverability: one can search for implementations of named methods, but not for
operator(). That's also a benefit of using polymorphism rather than templates or outright type erasure (à lastd::function).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Following my own logic, I should get rid of the lambda wrappers and force users to make their own subclasses. That would be better future-proofed against more methods, too (such as requiring subclasses to provide logging-related information).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess I'm not entirely clear on my own thinking here, though I think it's nice isolating users from having to do their own subclasses -- though admittedly lambda functions are likely an even more unfamiliar feature to a casual user...
Anyway, I guess what's there currently (class from lambda) is fine for the time being.