pyclaw
pyclaw copied to clipboard
solution.Solution -- cannot pass `frame=` as kwarg
I was having problems reading in a single frame of a solution using solution.Solution
and finally tracked it down to the fact that although the first parameter of Solution
is named frame
you cannot call it using this as a kwarg, e.g. the two commands below should load the same frame but the second silently does nothing:
>>> from clawpack.pyclaw.solution import Solution
>>> framesoln = Solution(0, path='_output', file_format='ascii')
>>> len(framesoln.states)
18
>>> framesoln = Solution(frame=0, path='_output', file_format='ascii')
>>> len(framesoln.states)
0
The reason is that the code decides whether to read the solution based on this logic:
if len(arg) == 1:
# Load frame
frame = arg[0]
Surely there's a better way to get the desired behavior but also allow specifying the key word?
And if this gets rewritten, would it be better to call this argument frameno
for consistency with what we do in visclaw and other places, or is frame
too heavily used elsewhere in pyclaw?
Yeah, the fact that the initialization does nothing if there are zero or 3+ arguments is very bad.
Looking through the code, frame
is used quite heavily; there are also instances of both frameno
and frame_number
....
Edit: and frame_num
. To be fair, I think this is the result of many different people editing the code.
It's no big deal to have different names for frameno
I think, but it would be nice to make it more robust to the manner in which it is called.
Looking at the current code I am not sure I see a way out of this dilemma without breaking something. Some thoughts though:
- Best solution would probably be to change all arguments to key word arguments with
frame
being first, which would then maintain the 1 argument version. - Maybe we could code around the 2 argument problem by checking the type of
frame
and treating it as a domain or patch although I am not entirely sure I remember. - We would want to maintain a specific set of key word arguments that would be explicitly handled in the
__init__
routine and pass the rest to the call toread
as is done now.
I think we can do something reasonable in a backward-compatible way:
- Add a case for
len(args)==0
, where we check which keywords have been specified - Also add an exception at the end so initialization never silently fails
If we are willing to make incompatible changes in the future, this could be revamped along the lines that Kyle suggests.
Agreed. Better to not break things.
I've implemented what I suggested in #636 . Could you both take a look?
That fixes the problem I was having at least. I haven't tested it more extensively.
I'm not sure what the recommended approach is more generally for cases like this where you want to be able to initialize in various different ways with the same __init__
function. We face something similar e.g. with topotools.Topography
where you could pass in both a path
to a topofile and a topo_func
that computes the topography instead. The path
doesn't actually get used unless you also calls the read
function. It's potentially confusing.
I think keyword arguments are probably the right thing to use for this type of functionality and has the benefit of being less confusing. The one problem is if someone uses incompatible keywords but then you could check for that.