Skip to content

Feb 2022 #69

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

Merged
merged 2 commits into from
Feb 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion _config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
# dc: Data Carpentry
# lc: Library Carpentry
# cp: Carpentries (to use for instructor traning for instance)
carpentry: "swc"
carpentry: "molssi"

# Overall title for pages.
title: "Python Scripting for Computational Molecular Science"
Expand Down
3 changes: 2 additions & 1 deletion _episodes/07-command_line.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ title: "Running code from the Linux Command Line"
teaching: 15
exercises: 10
questions:
- "How do I move my code from the interactive jupyter notebook to run from the Linux command line?"
- "How do I move my code from the interactive Jupyter notebook to run from the Linux command line?"
- "How do I make Python scripts with help messages and user inputs using argparse?"
objectives:
- "Make code executable from the Linux command line."
- "Use argparse to accept user inputs."
Expand Down
171 changes: 171 additions & 0 deletions _episodes/07-command_line_sys.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
---
title: "Running code from the Linux Command Line"
teaching: 15
exercises: 10
alternate: true
questions:
- "How do I move my code from the interactive Jupyter notebook to run from the Linux command line?"
- "How do I make simple Python scripts with user inputs using sys.argv?"
objectives:
- "Make code executable from the Linux command line."
- "Use sys.argv() to accept user inputs."
keypoints:
- "You must `import sys` in your code to accept user arguments."
- "The name of the script itself is always `sys.argv[0]` so the first user input is normally `sys.argv[1]`."
---
## Creating and running a python input file

We are now going to move our geometry analysis code out of the Jupyter notebook and into a format that can be run from the Linux command line. Open your favorite text editor and create a new file called "geom_analysis.py" (or choose another filename, just make sure the extension is .py). Paste in your geometry analysis code (the version with your functions) from your jupyter notebook and save your file.

The best practice is to put all your functions at the top of the file, right after your import statements. Your file will look something like this.
```
import numpy
import os

def calculate_distance(atom1_coord, atom2_coord):
x_distance = atom1_coord[0] - atom2_coord[0]
y_distance = atom1_coord[1] - atom2_coord[1]
z_distance = atom1_coord[2] - atom2_coord[2]
bond_length_12 = numpy.sqrt(x_distance**2+y_distance**2+z_distance**2)
return bond_length_12

def bond_check(atom_distance, minimum_length=0, maximum_length=1.5):
if atom_distance > minimum_length and atom_distance <= maximum_length:
return True
else:
return False

def open_xyz(filename):
xyz_file = numpy.genfromtxt(fname=filename, skip_header=2, dtype='unicode')
symbols = xyz_file[:,0]
coord = (xyz_file[:,1:])
coord = coord.astype(numpy.float)
return symbols, coord

file_location = os.path.join('data', 'water.xyz')
symbols, coord = open_xyz(file_location)
num_atoms = len(symbols)
for num1 in range(0,num_atoms):
for num2 in range(0,num_atoms):
if num1<num2:
bond_length_12 = calculate_distance(coord[num1], coord[num2])
if bond_check(bond_length_12) is True:
print(F'{symbols[num1]} to {symbols[num2]} : {bond_length_12:.3f}')
```
{: .language-python}

Exit your text editor and go back to the command line. Now all you have to do to run your code is type

```
$ python geom_analysis.py
```
{: .language-bash}
in your Terminal window. Your code should either print the output to the screen or write it to a file, depending on what you have it set up to do. (The code example given prints to the screen.)

## Changing your code to accept user inputs
In your current code, the name of the xyzfile to analyze, "water.xyz", is hardcoded; in order to change it, you have to open your code and change the name of the file that is read in. If you were going to use this code to analyze geometries in your research, you would probably want to be able to specify the name of the input file when you run the code, so that you don't have to change it every single time. These types of user inputs are called *arguments* and to make our code accept arguments, we have to import a new python library in our code.

Open your geometry analysis code in your text editor and add this line at the top.

~~~
import sys
~~~
{: .language-python}

Now that you have imported the `sys` library, you can use its functions. The library has a function called `sys.argv()` which creates a list of all the arguments the user enters at the command line. Everything after *python* is an argument, so `sys.argv[0]` is always the name of your script. We would like our code to accept the name of the xyz file we want to analyze as an argument. Add this line to your code.
```
xyzfilename = sys.argv[1]
```
{: .language-python}

Then you need to go the part of your code where you read in the data from the xyz file and change the name of the file to read to `xyzfilename`.
```
symbols, coord = open_xyz(xyzfilename)
```
{: .language python}

Save your code and go back to the Terminal window. Make sure you are in the directory where your code is saved and type
```
$ python geom_analysis.py data/water.xyz
```
Check that the output of your code is what you expected.


What would happen if the user forgot to specify the name of the xyz file? The way the code is written now, it would give an error message.
```
Traceback (most recent call last):
File "geom_analysis.py", line 22, in <module>
xyzfilename = sys.argv[1]
IndexError: list index out of range
```
{: .error}
The reason it says the list index is out of range is because `sys.argv[1]` does not exist. Since the user forgot to specify the name of the xyz file, the `sys.argv` list only has one element, `sys.argv[0]`. It would be better to print an error message and let the user know that they didn't enter the input correctly. Our code is expecting exactly two inputs: the script name and the xyz file name. The easiest way to add an error message is to check the length of the sys.argv list and print an error message and exit if it does not equal the expected length.

While you have practiced coding, you have probably seen many error messages. We can actually raise errors in our code and write error messages to our users.
```
if len(sys.argv) < 2:
raise NameError("Incorrect input! Please specify a file to analyze.")
```
{: .language-python}

This will exit the code and print our error message if the user does not specify a filename.

There are different types of errors you can raise. For example, you may want to raise a `TypeError` if you have data that is not the right type. If you want to learn more about raising errors, [see the official documenation from Python](https://docs.python.org/3/tutorial/errors.html)

We need to add one more thing to our code. When you write a code that includes function definitions and a main script, you need to tell python which part is the main script. (This becomes very important later when we are talking about testing.) *After* your import statements and function definitions and *before* you check the length of the `sys.argv` list add this line to your code.
```
if __name__ == "__main__":
```
{: .language-python}

Since this is an `if` statement, you now need to indent each line of your main script below this if statement. Be very careful with your indentation! Don't use a mixture of tabs and spaces!

Save your code and run it again. It should work exactly as before. If you now get an error message, it is probably due to inconsistent indentation.

```
import os
import numpy
import sys

def calculate_distance(atom1_coord, atom2_coord):
x_distance = atom1_coord[0] - atom2_coord[0]
y_distance = atom1_coord[1] - atom2_coord[1]
z_distance = atom1_coord[2] - atom2_coord[2]
bond_length_12 = numpy.sqrt(x_distance**2+y_distance**2+z_distance**2)
return bond_length_12

def bond_check(atom_distance, minimum_length=0, maximum_length=1.5):
if atom_distance > minimum_length and atom_distance <= maximum_length:
return True
else:
return False

def open_xyz(filename):
xyz_file = numpy.genfromtxt(fname=filename, skip_header=2, dtype='unicode')
symbols = xyz_file[:,0]
coord = (xyz_file[:,1:])
coord = coord.astype(numpy.float)
return symbols, coord


if __name__ == "__main__":

if len(sys.argv) < 2:
raise NameError("Incorrect input! Please specify a file to analyze.")


xyz_file = sys.argv[1]
symbols, coord = open_xyz(xyz_file)
num_atoms = len(symbols)

for num1 in range(0,num_atoms):
for num2 in range(0,num_atoms):
if num1<num2:
bond_length_12 = calculate_distance(coord[num1], coord[num2])
if bond_check(bond_length_12) is True:
print(F'{symbols[num1]} to {symbols[num2]} : {bond_length_12:.3f}')

```
{: .language-python}

{% include links.md %}
2 changes: 1 addition & 1 deletion _extras/about.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ title: About
</div>
</div>
<br/>
{% include carpentries.html %}
{% include molssi.html %}
{% include links.md %}
70 changes: 0 additions & 70 deletions _includes/carpentries.html

This file was deleted.

19 changes: 4 additions & 15 deletions _includes/favicons.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{% assign favicon_url = relative_root_path | append: '/assets/favicons/' | append: site.carpentry %}
{% assign favicon_url = site.baseurl | append: '/assets/favicons/' | append: site.carpentry | prepend: site.url %}

{% if site.carpentry == 'swc' %}
{% assign carpentry = 'Software Carpentry' %}
Expand All @@ -8,26 +8,15 @@
{% assign carpentry = 'Library Carpentry' %}
{% elsif site.carpentry == 'cp' %}
{% assign carpentry = 'The Carpentries' %}
{% elsif site.carpentry == 'molssi' %}
{% assign carpentry = 'The Molecular Sciences Software Institute' %}
{% endif %}

<!-- Favicons for everyone -->
<link rel="apple-touch-icon-precomposed" sizes="57x57" href="{{ favicon_url }}/apple-touch-icon-57x57.png" />
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="{{ favicon_url }}/apple-touch-icon-114x114.png" />
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="{{ favicon_url }}/apple-touch-icon-72x72.png" />
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="{{ favicon_url }}/apple-touch-icon-144x144.png" />
<link rel="apple-touch-icon-precomposed" sizes="60x60" href="{{ favicon_url }}/apple-touch-icon-60x60.png" />
<link rel="apple-touch-icon-precomposed" sizes="120x120" href="{{ favicon_url }}/apple-touch-icon-120x120.png" />
<link rel="apple-touch-icon-precomposed" sizes="76x76" href="{{ favicon_url }}/apple-touch-icon-76x76.png" />
<link rel="apple-touch-icon-precomposed" sizes="152x152" href="{{ favicon_url }}/apple-touch-icon-152x152.png" />
<link rel="icon" type="image/png" href="{{ favicon_url }}/favicon-196x196.png" sizes="196x196" />
<link rel="icon" type="image/png" href="{{ favicon_url }}/favicon-96x96.png" sizes="96x96" />
<link rel="icon" type="image/png" href="{{ favicon_url }}/favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="{{ favicon_url }}/favicon-16x16.png" sizes="16x16" />
<link rel="icon" type="image/png" href="{{ favicon_url }}/favicon-128.png" sizes="128x128" />
<meta name="application-name" content="{{ carpentry }} - {{ site.title }}"/>
<meta name="msapplication-TileColor" content="#FFFFFF" />
<meta name="msapplication-TileImage" content="{{ favicon_url }}/mstile-144x144.png" />
<meta name="msapplication-square70x70logo" content="{{ favicon_url }}/mstile-70x70.png" />
<meta name="msapplication-square150x150logo" content="{{ favicon_url }}/mstile-150x150.png" />
<meta name="msapplication-wide310x150logo" content="{{ favicon_url }}/mstile-310x150.png" />
<meta name="msapplication-square310x310logo" content="{{ favicon_url }}/mstile-310x310.png" />
<meta name="msapplication-TileColor" content="#FFFFFF" />
12 changes: 6 additions & 6 deletions _includes/lesson_footer.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,25 @@
<div class="col-md-6 help-links" align="right">
{% if page.source %}
{% if page.source == "Rmd" %}
<a href="{{repo_url}}/edit/{{ default_branch }}/{{page.path|replace: "_episodes", "_episodes_rmd" | replace: ".md", ".Rmd"}}" data-checker-ignore>Edit on GitHub</a>
<a href="{{site.github.repository_url}}/edit/gh-pages/{{page.path|replace: "_episodes", "_episodes_rmd" | replace: ".md", ".Rmd"}}">Edit on GitHub</a>
{% endif %}
{% else %}
<a href="{{repo_url}}/edit/{{ default_branch }}/{{page.path}}" data-checker-ignore>Edit on GitHub</a>
<a href="{{site.github.repository_url}}/edit/gh-pages/{{page.path}}">Edit on GitHub</a>
{% endif %}
/
<a href="{{ repo_url }}/blob/{{ source_branch }}/CONTRIBUTING.md" data-checker-ignore>Contributing</a>
<a href="{{ site.github.repository_url }}/blob/gh-pages/CONTRIBUTING.md">Contributing</a>
/
<a href="{{ site.github.repository_url }}/">Source</a>
/
<a href="{{ repo_url }}/blob/{{ source_branch }}/CITATION" data-checker-ignore>Cite</a>
<a href="{{ site.github.repository_url }}/blob/gh-pages/CITATION">Cite</a>
/
<a href="mailto:{{ site.email }}">Contact</a>
</div>
</div>
<div class="row">
<div class="col-md-12" align="center">
Using <a href="https://github.com/carpentries/styles/">The Carpentries style</a>
Using a MolSSI modification of <a href="https://github.com/carpentries/styles/">The Carpentries style</a>
version <a href="https://github.com/carpentries/styles/releases/tag/v9.5.0">9.5.0</a>.
</div>
</div>
</footer>
</footer>
29 changes: 29 additions & 0 deletions _includes/molssi.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{% comment %}
General description of The Molecular Sciences Software Institute.
{% endcomment %}
<div class="row">
<div class="col-md-2" align="center">
<a href="https://molssi.org/"><img src="{{ page.root }}/assets/img/molssi_main_logo.png" alt="MolSSI logo" width="100%" /></a>
</div>
<div class="col-md-8">
Since its launch in 2016, <a href="https://molssi.org/">The Molecular Sciences Software Institute</a>, has served as a nexus for the
broad computational molecular sciences community by providing software expertise, community engagement and leadership, and education
and training. Through a broad array of software infrastructure projects, teaching workshops, and community outreach, the MolSSI is
catalyzing the scientific advances needed to solve emerging scientific computing Grand Challenges.
</div>
</div>

<div class="row">
<div class="col-md-10">
<p>Education of students, post-docs, and faculty on programming and Best Practices in Software Development is a large part of MolSSI's mission.
Our education program consists of our cohorts of <a href="https://molssi.org/molssi-software-fellows/">Software Fellows</a>,
<a href="https://molssi-education.github.io/resources.html">online training materials</a>,
and <a href="https://molssi-education.github.io/request.html">multiple workshops online or in-person at various locations each year</a>.
</p>
<p>MolSSI’s education techniques and practices are modeled after <a href="https://software-carpentry.org/">The Software Carpentries</a>
style to teaching novice software best practices. This approach teaches subjects that not only increase a student’s scientific
capability and efficiency, but also his/her future marketability in both scientific and non-scientific fields.
</p>
</div>
</div>

Loading