Skip to content

Conversation

@tbertus
Copy link
Collaborator

@tbertus tbertus commented May 26, 2020

Hey Connor,

These are the changes we spoke about sometime ago. Caching the results of the predictor step so the convolution does not need to be repeated during the corrector steps. Let me know what changes you want me to make if you decide to accept this.

Thanks,
Thomas

@cglosser
Copy link
Owner

cglosser commented May 26, 2020

Hey Thomas -

I want to noodle a little bit more on getting rid of the first_call variable since flags get really ugly really fast. In the next day or two, if you can, try to find a copy of Uncle Bob's Clean Code (it's a really great reference, too!) and see what he has to say about flags. I don't think it'll be too hard to refactor evaluate into evaluate_past and evaluate_present or similar (in which case the calls to evaluate(first_call = false) just become evaluate_present()), but since you did the work here read Clean Code, see if the arguments in Clean Code justify a little more work, and, ultimately, make things easier to understand. I'll defer to your professional opinion :)

Connor

@tbertus
Copy link
Collaborator Author

tbertus commented May 27, 2020

I'll give it a look!

@tbertus
Copy link
Collaborator Author

tbertus commented Jun 2, 2020

I removed the first_call flag and separated the evaluate functions into first_evaluation_of_timestep and evaluate. I gave the functions chapter of Clean Code a look and wonder if you think the first_evaluation_of_timestep function could be further broken up.

@cglosser cglosser self-assigned this Jun 5, 2020
@cglosser
Copy link
Owner

cglosser commented Jun 11, 2020

One thing you can do if you're really feeling suave is to mention an issue number in your commit message. So, for instance, if you had said "Closes #42" in the commit where you got the Travis stuff working, Github would automatically link that together and close the issue. I think this is the coolest thing because if you're diligent about keeping issues around and mentioning them, it becomes super clear what you're trying to do (via the issues) and what commits address them (via the links).

coefficients_[pair_idx][0];
}
results += past_terms_of_results;
results += past_terms_of_convolution;
Copy link
Owner

Choose a reason for hiding this comment

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

Shouldn't this be return results + past_terms_of_convolution? If evaluate is called, and then evaluate_present_field is called, results gets multiple "presents" added to it, no?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

results is set to zero at the top of evaluate_present_field so it has the contribution of a one "present" at a time. As a matter of curiosity, because the function returns a reference, would return results + past_terms_of_convolution work?

Copy link
Owner

Choose a reason for hiding this comment

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

Ah, yes, ok - that makes sense. There's still something I'm not seeing, though; ultimately I think evaluate is really just past_part + present_part. You've moved the logic for present_part into the other function but you don't call it here so I'm not sure what your intention is.

And you're exactly right: using return results + past; returns what's called an "rvalue" (so a reference to it is invalid). Coding it that way would require changing the function signature.

Copy link
Collaborator Author

@tbertus tbertus Jun 16, 2020

Choose a reason for hiding this comment

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

I am not sure I follow. As of now, evaluate computes and stores the past and adds it to the present. evaluate_present_field zeros out results computes the updated present contribution and adds it to the past_part that was calculated by evaluate

Copy link
Owner

@cglosser cglosser Jun 18, 2020

Choose a reason for hiding this comment

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

Hmm, so, in my head evaluate should always give you the full field (perhaps inefficiently) because it's the virtual function prescribed by InteractionBase. "An interaction can always be evaluated", so to speak, and you may not need the complexity of divorcing the past update from the present update (if, say, you're not using something as sophisticated as the predictor/corrector). So I think a good design here would be to implement two methods, ResultArray &AIM::DirectInteraction::evaluate_past and resultArray &AIM::DirectInteraction::evaluate_present, that each populate an internal array of known size (like results). Then you'll have something like this:

const InteractionBase::ResultArray &AIM::DirectInteraction::evaluate(const int time_idx) {
    evaluate_past(time_idx);
    evaluate_present(time_idx);
    results = past_results + present_results;
    return results;
}

and

const InteractionBase::ResultArray &AIM::DirectInteraction::evaluate_present(const int time_idx) {
    // Assumes evaluate_past has already been called for this time_idx
 
    // <Logic for present update>
    results = past_results + present_results;
    return results;
}

To do this, it may make sense to hoist evaluate_past and evaluate_present into InteractionBase as pure virtual functions. This would be the case if every Interaction that ever exists must define those functions to make sense (which is probably the case).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I agree that would be a good way to set things up. But to be clear evaluate can currently be used independent of evaluate_present_field. The functionality of evaluate now is the same as it was originally with the added side effect of storing past_results.

Comment on lines 16 to 17
const ResultArray &evaluate(const int) final;
const ResultArray &first_evaluation_of_timestep(const int) final;
const ResultArray &evaluate_present_field(const int) final;
Copy link
Owner

Choose a reason for hiding this comment

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

I really really hate that, e.g., direct_interaction needs to change to accommodate a new requirement in the predictor/corrector. It indicates things are too tightly coupled together. I'm sure there's a better way to push all of this into the integrator (probably with a strategy), but I'm not sure how to do it yet.

coefficients_[pair_idx][0];
}
results += past_terms_of_results;
results += past_terms_of_convolution;
Copy link
Owner

@cglosser cglosser Jun 18, 2020

Choose a reason for hiding this comment

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

Hmm, so, in my head evaluate should always give you the full field (perhaps inefficiently) because it's the virtual function prescribed by InteractionBase. "An interaction can always be evaluated", so to speak, and you may not need the complexity of divorcing the past update from the present update (if, say, you're not using something as sophisticated as the predictor/corrector). So I think a good design here would be to implement two methods, ResultArray &AIM::DirectInteraction::evaluate_past and resultArray &AIM::DirectInteraction::evaluate_present, that each populate an internal array of known size (like results). Then you'll have something like this:

const InteractionBase::ResultArray &AIM::DirectInteraction::evaluate(const int time_idx) {
    evaluate_past(time_idx);
    evaluate_present(time_idx);
    results = past_results + present_results;
    return results;
}

and

const InteractionBase::ResultArray &AIM::DirectInteraction::evaluate_present(const int time_idx) {
    // Assumes evaluate_past has already been called for this time_idx
 
    // <Logic for present update>
    results = past_results + present_results;
    return results;
}

To do this, it may make sense to hoist evaluate_past and evaluate_present into InteractionBase as pure virtual functions. This would be the case if every Interaction that ever exists must define those functions to make sense (which is probably the case).

temp_observers += src.rowwise() * prop.transpose();
}

// set observers equal to temp_observers
Copy link
Owner

Choose a reason for hiding this comment

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

Please don't repeat code in comments; why do you need to set observers equal to temp_observers?


Eigen::Vector3d pulse_vector; // is the convention to declare and define in
// the same line?
Eigen::Vector3d pulse_vector;
Copy link
Owner

Choose a reason for hiding this comment

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

You could declare and define simultaneously if you think it's cleaner. I think I split it up because there's a lot going on there, particularly with the ternary rotating-frame if statement.

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