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

Review June 2020 #26

Open
wants to merge 438 commits into
base: review/project_review_june_2020
Choose a base branch
from
Open

Conversation

mads-bertelsen
Copy link
Collaborator

As this project has not been properly reviewed, it is necessary to make a complete review in order to assure quality.
Due to the size of the task, it is broken down to smaller tasks which are described on Jira to manage the work. This pull request will be used for adding review comments and improvements along the way without affecting the master branch.

@codecov
Copy link

codecov bot commented Jun 8, 2020

Codecov Report

❗ No coverage uploaded for pull request base (review/project_review_june_2020@6f4c989). Click here to learn what that means.
The diff coverage is n/a.

Impacted file tree graph

@@                        Coverage Diff                         @@
##             review/project_review_june_2020      #26   +/-   ##
==================================================================
  Coverage                                   ?   90.61%           
==================================================================
  Files                                      ?       37           
  Lines                                      ?     6444           
  Branches                                   ?        0           
==================================================================
  Hits                                       ?     5839           
  Misses                                     ?      605           
  Partials                                   ?        0           

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 6f4c989...52e2fae. Read the comment docs.

Copy link
Contributor

@nvaytet nvaytet left a comment

Choose a reason for hiding this comment

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

In summary, there is not much wrong with the plotting code itself, but there is a lot of duplication.
I strongly recommend to make it so that make_sub_plot and make_animation are just calling make_plot.

I would also try to split this in multiple files to make it easier to navigate. You don't have many files in that directory, so it should be fine to so that (i know that when you start getting many many files, it also starts to make things difficult to follow, but you are not there yet).


index = -1
for data in data_list:
index = index + 1
Copy link
Contributor

Choose a reason for hiding this comment

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

index doesn't appear to be used in this loop

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Did a larger refactoring of the plotting functionality, this silly issue was removed. I can't respond directly to the top comment / summary, but the plotting code was refactored to remove the code duplication. The new version has been pushed to the master branch. The three classes are now all functions, which depend on a simpler underlying function. This also allowed easier and better testing!


print("Plotting data with name " + data.metadata.component_name)
if type(data.metadata.dimension) == int:
fig = plt.figure(0)
Copy link
Contributor

Choose a reason for hiding this comment

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

i recommend to get the axes as an object here, as the get current axes method gca is always prone to break, since some other axes somewhere else may become the current active axes.

Suggested change
fig = plt.figure(0)
fig, ax0 = plt.subplots()

this way you are always sure which axes you are dealing with

Copy link
Contributor

Choose a reason for hiding this comment

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

and maybe give the option to change the figsize? plt.subplots(figsize=(12, 8)) (width and height in inches)
should be passed from input arguments?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This change was included in the refactor, the internal plotter function now uses fig and ax in the arguments which is provided by the calling function. In that way plt.gca is never used, and the ax object is used for all plotting.

figsize is now also a keyword argument in all three plotting functions.

y_err = data.Error

#(fig, ax0) = plt.errorbar(x, y, yerr=y_err)
plt.errorbar(x, y, yerr=y_err)
Copy link
Contributor

Choose a reason for hiding this comment

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

Then call the methods on the axes, to also be sure they are plotting on the correct axes

Suggested change
plt.errorbar(x, y, yerr=y_err)
ax0.errorbar(x, y, yerr=y_err)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

All plotting is now done through the ax object instead of plt directly. Great suggestion.

#(fig, ax0) = plt.errorbar(x, y, yerr=y_err)
plt.errorbar(x, y, yerr=y_err)

ax0 = plt.gca()
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
ax0 = plt.gca()

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Avoided by giving the internal plotting function the ax as an argument.

data.metadata.limits[1]*x_axis_mult)

# Add a title
plt.title(data.metadata.title)
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
plt.title(data.metadata.title)
ax0.set_title(data.metadata.title)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done!

plt.show()


class make_sub_plot:
Copy link
Contributor

Choose a reason for hiding this comment

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

I think the recommended convention is to have uppercase letters in class names, and underscores in function names.
e.g. MakeSubPlot.

Copy link
Contributor

Choose a reason for hiding this comment

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

That said, this class only has an __init__ and no other functions associated to it.
Maybe it can just be a function? Does it need to be a class? Is another class inheriting from it somehere?
If not, I would just make it a function, if it is just making a figure. Matplotlib will take care of keeping the figure alive for you, it doesn't need to be in a class. Especially since I don't really see the use of self anywhere in here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

make_plot, make_sub_plot and make_animation are now all functions!

plt.show()


class make_animation:
Copy link
Contributor

Choose a reason for hiding this comment

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

In this case this might be useful to have a class, see below

# initialize plots

data = data_list[0]
if isinstance(data.metadata.dimension, int):
Copy link
Contributor

Choose a reason for hiding this comment

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

Again i would try to just call make_plot here instead of duplicating again.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Made a internal plotting function instead that make_plot, make_sub_plot and make_animation all use. The amount of duplication was really bad, glad to be rid of it!


def init_1D():
# initialize function for animation
er.set_data([], [], []) # wont work
Copy link
Contributor

Choose a reason for hiding this comment

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

I would be very careful using functions inside function to access the scope of the __init__. I would either

  • pass things as arguments (e.g. er needs to be an input argument and this function needs to be outside of the class)
  • or use the fact that we are inside a class, and make er a member of the class (self.er = ...) and then make these functions class members, i.e.
def init_1D(self):
    self.er = set_data([], [], [])

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The FuncAnimation need a function for updating the data with only an index as argument (unless I am misunderstanding), and in such I need to define a short function inside this function. I find it acceptable since the function is so short.


class make_sub_plot:
"""
make_plot plots contents of McStasData objects
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
make_plot plots contents of McStasData objects
make_sub_plot plots contents of McStasData objects

copied and pasted from above?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Added additional text, but make_plot and make_sub_plot are still close in their description, as the functions are rather similar.

Copy link
Contributor

@nvaytet nvaytet left a comment

Choose a reason for hiding this comment

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

I can see that you have tests for the plot options, but can't really see tests on the plotting functions.
I know testing plotting is incredibly difficult, but maybe a first step would be to just call the plotting functions with different parameters, and simply make sure they don't throw any errors.
It would be a start.


def test_McStasPlotOptions_default_orders_of_mag(self):
"""
Test that newly created McStasMetaData has correct type
Copy link
Contributor

Choose a reason for hiding this comment

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

These docstrings all the same for all tests. Should be updated.

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 have expanded the testing of McStasPlotOptions (only some options were tested), and updated their doc strings.
In addition a new file was added, test_Plotter.py, which adds 600 lines of testing for the plotting functionality, including running the internal plotter function (without plt.show() ) for all 1D/2D and log mode combinations.

This could obviously still be improved in the sense that actual plotting is not tested or visually compared to a known correct plot.

mads-bertelsen and others added 27 commits November 29, 2021 09:06
Include parameter information in metadata object. Will add a parameter attribute in extrat_info to make the parameters easily accessible from the McStasData object.
Added a metadata field.
Added unittests.

Also added __repr__ to configurator class.
Use these as baseclasses for the appropriate classes in McStasScript,
and have made all required changes for the package to be functional.
Unit tests are updated and everything seems to be in order.

Am using direct access to underlying attributes a lot, so it is
hard to change the underlying implementation in libpyvinyl without
breaking this implementation. Will probably need a better contract
with libpyvinyl to be more confident in the features I use will not
be changed.
Syntax for running simulations have changed.

Instrument class now based on libpyvinyl calculator.
Jupyter notebook example available in example folder.

Still todo:
Update doc strings
Update unit tests
Update documentation
Allow components to use parameter objects for input.
Updated example to show the new use.
method. It was required to register dynamic classes to globals.

Avoid overwriting loaded classes by checking if attribute already
defined.

Updated example to show dump and load from dump.
McStasScript use a parameter class that inherits from the libpyvinyl
parameter class as more checks and methods are needed. When importing
from libpyvinyl versions, all attributes are included and the checks
are made.
Now use output_path from BaseCalculator for folder name

Set increment_folder_name to True as default.
Still work to do on documentation, need to ensure all doc strings are
up to date, handle README and the pdf documentation.
Ensured declare and parameter variables print nicely in component print
Settings are now a dict which is updated by the settings method.
The settings method contains input sanitation and is used in init.

All examples and tutorials were checked for proper new usage.

The components can now have parameters set with set_parameters
using both keyword and dict argument.
Avoided using parameters in settings, these have to go through
libpyvinyl parameters now. This changed interface a bit, but no
issue.
and increment_folder_name set to False. Before it would load such
a data file, which made unittests easier but was confusing for users.
Now it will provide an error if the folder already exists.
Furthermore, if a simulation does not produce a folder, a warning is
shown, and the data returned is None.

Tests updated to take this into account.
Started helper file for tests.
Tests include a case of setting seed.

Updated interface to disable threading as it caused crashes.
Mads Bertelsen and others added 30 commits December 1, 2023 14:52
Removed intensity from event data, as it was only used in plotting, and now plotting of event data has been removed.
Improved reliability of reading NeXus data
…rs if run from within a related 'environment'
Simple solution to auto-detect McStas and McXtrace config
Allow McXtrace sessions to visualize using mxdisplay(-webgl/-pyqtgraph)
…r of components.

When search statements are cleared, the component database is reset.
…r both parameters, AT and ROTATED.

The system can be used for debugging, may be used for some automatic functions in the future.
Fix bad escape sequence warning in python 3.11
… instead of near DEFINE and parameters. This syntax works where the others have issues.
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.

4 participants