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

Oscillations like fortran #42

Merged
merged 35 commits into from
Aug 5, 2024
Merged

Oscillations like fortran #42

merged 35 commits into from
Aug 5, 2024

Conversation

HannoSpreeuw
Copy link
Contributor

@HannoSpreeuw HannoSpreeuw commented Jul 25, 2024

Fixes #39 and #40.

The 'typo' is not included here, any manipulation of the porosity diffusion coefficient should be done by modifying b.
However, b is set here to a default value of 5e-4, not to the rhythmite value. I.e. after merging with main and then running, marlpde will not yield oscillations within 250 ka.

Initial and surface porosity are both set to 0.8, i.e. different from Scenario A.

There is an ongoing discussion on how to apply the FV scheme. It would be neat to settle that discussion asap. If the conclusion would be that we interpret the FV (1977) paper in such a way that this allows for the same spatial derivative to be calculated in two different ways for the same rhs, then an additional commit is required that would align marlpde with rhythmite in this respect. It would be nice if we could add that commit before this PR is merged. The FV paper is not an easy paper to interpret, though.

EmiliaJarochowska and others added 17 commits June 11, 2024 19:37
…e. until 133.854 ka at which point it will give a RuntimeError: Time step below 1e-10.
…on dataclass. We want to use the scipy solver, i.e. solve_ivp, since it provides for implicit methods. Also we want to integrate over 250_000 years, so we should set the progress tracker interval accordingly.
…tions 35 from L'Heureux, but py-pde demands left and right boundary conditions. This means that 'curvature : 0' is a dummy boundary condition. We will make sure that it does not leak into our integration, by using only backward differencing for the spatial derivatives in the right-hand sides of the time derivative equations for CA and CC."

This reverts commit 0d8191f.
…does indeed yield oscillations within 250 ka. Note that this commit includes a reversal of commit 0d8191f meaning that the 'dummy' bottom boundary conditions are reintroduced: self.bc_CA = [{'value': CA0}, {'curvature' : 0}]; self.bc_CC = [{'value': CC0}, {'curvature': 0}].
… a means of imposing boundary conditions. This seems a way to align with rhythmite when U changes sign, i.e. becomes negative (upward flow) which requires a bottom boundary condition for Araganonite and Calcite if one wants to keep applying the upwind solution. That bottom boundary condition is not available. The way rhythmite solves this is to apply a downwind solution, just for the deepest node, while all the other nodes retain upwind. Effectively this means that the spatial gradients for Aragonite and Calcite for the deepest node are equal to that gradient for the second deepest node. This is what I am trying to mimic here using 'adjacent_value'. Anyway, with the use of the 'typo' for the porosity diffusion coefficient this results in oscillations within 250 ka.
… boundary condition for Aragonite and Calcite.

This reverts commit 73b91b4.
… a flat profile on aragonite and calcite at the bottom instead of a second spatial derivative=0 or a a first spatial derivative=adjacent_value.
…ss, added to the Fortran code, that inspired parameters.py. They are redundant for marlpde, so they were removed. Secondly, the dimension of the grid was doubled to check its possible effect on oscillations while retaining the depth resolution.
…ndition for aragonite and calcite makes oscilations vanish, similar to the slope=0 ad-hoc bottom boundary condition.
…nditions and porosity diffusion coefficient - that will yield oscillations independent of the grid size, retaining grid resolution.
…ed to an IDE is personal and should not be part of this repo.
…It includes removing the typo; any mainpulation of the porosity diffusion coefficient should be done through modifying b. Canonical values of phi0 and phi00 are now 0.8 and 0.8, respectively. These differ from Scenario A, but we are looking for oscillations. We will investigating larger grids and a higher grid resolution, but we will set for 5m and 2.5cm as default values. The default ad-hoc bottom boundary conditions for aragonite and calcite were chosen to align with rhythmite. This means that the spatial derivatives of the deepest and second deepest node are equal. If two adjacent spatial derivatives are equal, this means that the second spatial derivative is equal to 0. This is reflected by {'curvature': 0}.
@EmiliaJarochowska
Copy link
Contributor

Before getting to the discussion on F-V, I want to comment on this:

However, b is set here to a default value of 5e-4, not to the rhythmite value. I.e. after merging with main and then running, marlpde will not yield oscillations within 250 ka.

The "default value" of 5e-4 is clearly assigned so as to compensate for the typo. It has no other motivation. If we do not use the typo and use the correct expression for the porosity diffusion coefficient, this value has no meaning. I believe we can leave it there though as the choice of b leading to oscillations is a matter of setting a parameter by the user.

HannoSpreeuw and others added 8 commits July 25, 2024 14:56
…TkAgg' which requires the 'tk' interactive framework, as 'headless' is currently running. This should fix that.
…ntegrating-diagenetic-equations-using-Python into Oscillations_like_Fortran
… It is set here equal to T* to make a comparison with ground truth data - needed for the regression tests - easier. But obviously, to see any oscillations, one will need to set it to somthing (much) larger.
…ve been changed, to facilitate the search for oscillations.
Copy link
Contributor

@EmiliaJarochowska EmiliaJarochowska left a comment

Choose a reason for hiding this comment

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

I am stuck on this because I don't understand the relationship between method="LSODA" and

    solver: str   = "scipy"
    scheme: str   = "euler"

LSODA uses Adams and BDF methods, so that's clearly not Euler. So which one will be used?

@HannoSpreeuw
Copy link
Contributor Author

HannoSpreeuw commented Jul 30, 2024

Confusing indeed!

solver: str = "scipy" is actually not the solver but the solve_ivp module, as wrapped by py-pde.

What I am struggling with is that in main we currently have

    solver: str   = "explicit"
    scheme: str   = "rk"

Which will launch py-pde's native explicit solver with a Runge-Kutta scheme.

However, scheme is not an argument of the ScipySolverclass.
I.e. once you have chosen solver: str = "scipy", whatever you enter as scheme will not be propagated. Same for retinfo and adaptive. They have become meaningless in that case.

Somewhat related: it may be good to add a line to the Solver class, with method = "LSODA", since that is now hardcoded in the eq.solve call.
I can add this.

@EmiliaJarochowska
Copy link
Contributor

I think what you proposed is indeed the best solution. But it would be useful to include a warning somewhere if anyone (including our future selves) tries to change the solver. E.g. I already encountered that confusion that changing the adaptive parameter didn't work and only after your explanation I understand why.
Maybe it also needs an update of the README , because currently it says:

After two minutes you should see plots similar to figure 3e from L'Heureux (2018).

@HannoSpreeuw
Copy link
Contributor Author

I.e. once you have chosen solver: str = "scipy", whatever you enter as scheme will not be propagated. Same for retinfo and adaptive. They have become meaningless in that case.

Sorry, not entirely correct, retinfo: bool = True is propagated in the solver call through ret_info=kwargs["retinfo"] since it is an argument of the solver method of the pde base class.

…ike 'backend' and 'ret_info' will always be propagated, but 'scheme' and 'adaptive'only for py-pde's native 'explicit' solver.
@EmiliaJarochowska
Copy link
Contributor

I ran after this change with the following options:

    solver: str   = "explicit"
    scheme: str   = "euler"
    adaptive: bool = False

and got

Traceback (most recent call last):
  File "/opt/homebrew/lib/python3.11/site-packages/pde/solvers/base.py", line 91, in from_name
    solver_class = cls._subclasses[name]
                   ~~~~~~~~~~~~~~~^^^^^^
KeyError: 'LSODA'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/emilia/Documents/Documents - UU101746/GitHub/Integrating-diagenetic-equations-using-Python/marlpde/Evolve_scenario.py", line 143, in <module>
    integrate_equations(**all_kwargs)
  File "/Users/emilia/Documents/Documents - UU101746/GitHub/Integrating-diagenetic-equations-using-Python/marlpde/Evolve_scenario.py", line 96, in integrate_equations
    sol, info = eq.solve(state, t_range=End_time, dt=dt, \
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/pde/pdes/base.py", line 582, in solve
    solver_obj = SolverBase.from_name(solver, pde=self, **kwargs)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/lib/python3.11/site-packages/pde/solvers/base.py", line 99, in from_name
    raise ValueError(
ValueError: Unknown solver method 'LSODA'. Registered solvers are 'AdaptiveSolverBase', 'explicit', 'explicit_mpi', 'implicit', 'scipy'

probably not relevant but I also set b: quantity = 1.066667 / u.kPa

…. Running marlpde as from the previous commit led to 'RuntimeError: Encountered Not-A-Number (NaN) in evolution' (this is with 'LSODA'). Stability was added by providing the equivalent of a Jacobian sparsity matrix for 'LSODA' which is done by setting 'lband' and 'uband'. This not only provided stability but also led to a substantial performance increase!
@HannoSpreeuw
Copy link
Contributor Author

Yeah, method='LSODA' is not an explicit solver, more specifically, not provided by py-pde's native 'explicit' solver method, you should choose solver: str = "scipy" if you want to apply 'LSODA'.

@EmiliaJarochowska
Copy link
Contributor

But what should be set if I want to use solver: str = "explicit"? Then method is not applicable.

@HannoSpreeuw
Copy link
Contributor Author

HannoSpreeuw commented Aug 2, 2024

You got me there...
I guess I need to add a conditional to the eq.solve call.
Let me get back to you after lunch.

…ny solver other than 'scipy' is chosen: it is not a keyword argument in that case. This commit will fix that with a conditional --> two separate calls. Not the most elegant soluttion, it would be nicer to apply some filtering of the arguments, or by using a ternary operator, but I did not find a way to get that working for keyword arguments; for positional arguments a ternary operator should work.
@HannoSpreeuw
Copy link
Contributor Author

Pls try again.

@HannoSpreeuw
Copy link
Contributor Author

I found a more elegant solution on SO, using the post_init method from Python dataclasses.

Btw, we already apply post_init in the Map_Scenario method.

Copy link
Contributor

@EmiliaJarochowska EmiliaJarochowska left a comment

Choose a reason for hiding this comment

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

Everything seems to work fine! Thank you :-)

@HannoSpreeuw
Copy link
Contributor Author

Let me try to fix that: it would be nice if Figure 3 would be reproduced with our default settings.

@HannoSpreeuw
Copy link
Contributor Author

I found a more elegant solution on SO, using the post_init method from Python dataclasses.

Btw, we already apply post_init in the Map_Scenario method.

Let me just add that commit.

@HannoSpreeuw
Copy link
Contributor Author

Let me try to fix that: it would be nice if Figure 3 would be reproduced with our default settings.

O wait, that is perhaps as it should be, since we now have 0.8 as initial and surface default porosity.

@EmiliaJarochowska
Copy link
Contributor

You changed the phis to 0.8 because we wanted potential users to find the conditions that lead the oscillations, as this is the point of interest of this model. Fig. 3 is more trivial, so to say. I guess if anyone uses this model, it is primarily to look at the oscillations. But it boils down to what the documentation would be. Have you thought of what the documentation will be? Our SMP has this field empty. I guess a simple documentation generated from your excellent docstrings with some preamble will be sufficient, as we can refer to the paper on the model itself.

@HannoSpreeuw
Copy link
Contributor Author

Sounds like a plan!

@EmiliaJarochowska
Copy link
Contributor

Then I think this branch can be merged and I'll assign you to another issue for the documentation

…cationWarning: Jupyter is migrating its paths to use standard platformdirs given by the platformdirs library. To remove this warning and see the appropriate new directories, set the environment variable and then run .' To execute the second command, 'jupyter', as an executable, was needed, but it was not available, so I pip installed it.
…ices for 'solver', 'method', 'scheme' and 'adaptive'. Actually, it is about more than incompatible choices, it is about certain Solver() attributes that should be complete removed before they are passed as an argument to the eq.solve(...) call in Evolve_Scenario.py. E.g. for the 'explicit' solver, one cannot also provide a 'method', while this is possible for the 'scipy' solver. In a previous commit, a conditional was added before the eq.solve call, but that was considered not the most elegant solution; filtering out attributes in a __post_init__ method of the Solver dataclass seems more sustainable, since it can handle increasing complexity and that is what is achieved through this commit. Also some better formatting in the dataclasses (removing spaces). Next, the dict from asdict(Solver()) is passed 'as is' to the eq.solve call, where we previously had End_time = kwargs[tmax]/Tstar before the eq.solve(state, t_range=End_time, ...) call. That is why we no longer have in tmax in years in the Solver dataclass, but t_range: int = 1 instead, i.e. a default value of 1 times T*. Likewise retinfo--> ret_info (since we no longer have the 'conversion' of keyword names in the eq.solve call. Last but not least, the number of cells (nodes), denoted by N, is not an eq.solve argument and therefore had to be removed from the Solver dataclass and added to the Map_Scenario method, more specifically the 'post_init' therein.
…e 'method' atrribute from the Solver dataclass has been filtered out in its __post_init__, e.g. because the user has chosen solver: str = 'explicit', then we can no longer have a method=something, not even method=None keyword argument in the eq.solve call, unless we keep the conditional we have around the eq.solve call. However, that is not a sustainable solution with increasing incompatibility issues within the Solver dataclass. Instead we want an eq.solve call like this: eq.solve(state, **solver_parms, tracker_parms, liveplots, ...). That requires not concatenating all arguments of integrate_equations in the single 'kwargs' dict, but keeping 'solver_parms', 'tracking_parms' and 'pde_parms' separate. But we do want to record all metadata in the stored hdf5 file, that is why we still need a concatenation 'storage_parms=solver_parms | tracker_parms | pde_parms' and 'storage = FileStorage(stored_results, info=storage_parms)'.
… instead of kwargs, i.e. one (concatenated) dict, we need to adjust the regression tests to handle this.
…' implicit methods from solve_ivp, but not working, no clue why. The integration does not progress in time, but does not crash. The implementation should be identical to commit 2197188 from the Use_solve_ivp_without_py-pde_wrapper branch. In that branch this Jacobian sparsity matrix works succesfully. Lines 'jac_sparsity: csr_matrix = None' in the Solver dataclass and 'self.jac_sparsity = jacobian_sparsity()' in its __post_init__ method removed, to avoid users applying this Jacobian sparsity matrix implementation for now.
@HannoSpreeuw HannoSpreeuw merged commit 54187b5 into main Aug 5, 2024
1 check failed
@HannoSpreeuw HannoSpreeuw deleted the Oscillations_like_Fortran branch August 5, 2024 19:02
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.

Reproduce oscillations caused by a typo in Eq 25 in the Fortran code
2 participants