-
Notifications
You must be signed in to change notification settings - Fork 35
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 of update_nodes
#381
Conversation
I like the initiative 😄 I would avoid the snake_case tho (according to the current coding guidelines ... 😉). Agree on removing the "_classes", would also remove underscores and upper cases in any module classes, simplify the names for the sweeper module (e.g from pySDC.implementations.sweepers import Implicit, IMEX, MultiImplicit Also, I would not specify the ODE order for the sweeper when first order, since it is how SDC is implemented by default ... |
I agree with @tlunet. This would remove quite a few poor initial-stage decisions, indeed. Further comments:
|
Actually, the abstraction is meant to aid seeing the algorithm in the code. So, for instance, instead of # gather all terms which are known already (e.g. from the previous iteration)
# this corresponds to u0 + QF(u^k) - QdF(u^k) + tau
# get QF(u^k)
integral = self.integrate()
for m in range(M):
# get -QdF(u^k)_m
for j in range(1, M + 1):
integral[m] -= L.dt * self.QI[m + 1, j] * L.f[j]
# add initial value
integral[m] += L.u[0]
# add tau if associated
if L.tau[m] is not None:
integral[m] += L.tau[m] I want to call a function def build_right_hand_side(self):
rhs = self.initialize_right_hand_side_buffer()
self.add_Q_minus_QD_times_F(rhs)
self.add_initial_conditions(rhs)
self.add_tau_correction(rhs)
return rhs The idea is that you can actually read the algorithm in the code! Perhaps an even better example is the
The differences, on the other hand are only in adding Concerning renaming |
Regarding computing the residual only once, I actually vote against it. It assume it is more efficient to compute |
|
So, this could be a similarity? In general, a system |
This was exactly my reasoning for suggesting the rename. I understand now that Euler is perhaps not general enough, however solve_system is a bit too unspecific for my liking. But we don't need to fix that now. If someone comes up with a good idea, we can change it. Otherwise, we can just leave it as is for now. |
Well, the only purpose of this function is to "solve a system". This does not have a focus on time-stepping (at least from the perspective of the API), but on linear or nonlinear solvers. This is why it is called |
Ok, let's forget about renaming for a moment and focus on this PR. :D @pancetta, how do you feel about it? How do you feel about moving some of the changes to the core module? I am not offended if you don't feel favourably :D |
I like the renaming of things and a certain level of abstraction, too, but I'm not sure how this would look like camelCase-style, esp. those long descriptive function names. What do you think @tlunet, @brownbaerchen? |
Do you also prefer underscores? I thought I was the only one at this point :D But I will give in to the camel case crowd if needed. I can see some benefit in that... |
Not really sure about this thing : def build_right_hand_side(self):
rhs = self.initialize_right_hand_side_buffer()
self.add_Q_minus_QD_times_F(rhs)
self.add_initial_conditions(rhs)
self.add_tau_correction(rhs)
return rhs When each method uses basically one or two line of code, it's probably better to have a comment in the code and then the line. I think the previous version is most understandable |
I guess I do prefer underscores (as you can see all over pySDC), but I understand @tlunet's point in the contributors guide. I don't care that much, but for long function names underscores might be better to read. |
Concerning the which is the base solver for any Backward Euler step, or any implicit SDC update, etc ... Since it's linked to the problem itself (and it's f function), why not simply something like this : def solve(self, alpha, b, t):
r"""
Solve the following (non-) linear system :
.. math::
u - \alpha F(u, t) = b
with :math:`F(u,t)` the RHS function of the problem.
Parameters
----------
alpha : float
The :math:`\alpha` coefficient
b : dtype_u
Right-hand side :math:`b` for the system
t : float
Time of the solution.
Returns
-------
u : dtype_u
Solution of the (non-)linear system
"""
raise NotImplementedError() Then it can be used for any implicit updated in pySDC. and also as for the generic exact solution implemented by @brownbaerchen, we could have a generic implementation of this method in the base PS : on a side note, I agree that long function name are not great in camelCase, but I also believe that long function name are not great either. So forcing camelCase also forces to find names that are not too long, not too short (which is not very enforced when we allow ourselves snake_case ...) Also, nothing prevents to add a short line of documentation in the header of the function, ex : def doSomething(self):
"""Do the something we talked about""" |
Yes, I agree! Both with the |
Since my spider sense picks up only limited enthusiasm about this style of coding, I will stop with the refactoring for now ;) |
I started reading a book called Clean Code and decided to give some of the things I learned a shot. I refactored the
update_nodes
functions in the generic_implicit and imex_1st_order sweepers. Feel free to reject this. What constitutes good code is subjective after all and I can see how someone could find the current version more readable than what I suggest.The principles I tried to apply are mainly:
A benefit gained from the abstraction of small functions is that the imex sweeper can be shortened significantly. Of course, this comes at the expense of difficulty to understand abstract code. You cannot really understand what the imex sweeper does without considering the above level of abstraction. On the other hand, the differences to the generic implicit sweeper become much more clear as you don't really need to implement any shared functionality.
If you like these changes, I would apply them to some of the other sweepers as well and I believe that the explicit sweeper or the RK sweeper can be shortened significantly as well. In this case, it would be worth considering moving some changes to the core sweeper module.
At first I didn't want to start refactoring existing code. However, I may have to start taking memory a bit more seriously and this is a side effect of these changes. Memory usage is indeed reduced because the current implementation generates a right hand side for the problem to solve from a copy of the "integral" at the respective node. In this PR, I want to change that to construct a right hand side vector from the integral and so on and directly pass this to the solver.$(Q-Q_\Delta)F$ instead of $QF - Q_\Delta F$ as in the current implementation. I actually have a separate copy of these sweepers in my project folder right now which implement this already. By refactoring some other stuff a bit more, I could get rid of them and we could have the more efficient implementation as default.
Another side effect is gained efficiency because I suggest to compute
Please do comment! Do you like this style of coding or do you like a different level of abstraction that generates more self-contained files? Do you require comments and docstrings? Again, I don't claim to have objectively improved the prettiness of the code in this PR.
While we're on the topic of more expressive naming, I suggest to rename a few things, for instance
integrate
->integrate_right_hand_side
solve_system
->Euler_step
description
->configuration
pySDC.implementations.sweepers.generic_implicit
etc.How do you feel about such things?