Skip to content

Commit

Permalink
Merge pull request #303 from xylar/update_conda_package_for_mpi
Browse files Browse the repository at this point in the history
Update conda package and compass to better support MPI

This merge makes two scripts into stubs so that the functionality from the scripts is fully available through functions within the `mpas_tools` package:
* The functionality of the paraview VTK extractor is now in `mpas_tools.viz.paraview_extractor`
* Creation of SCRIP files from MPAS meshes is now in `mpas_tools.scrip.from_mpas`
The scripts for this functionality still exist but are just stubs that call the functions.

These changes allow VTK and SCRIP files to be created directly within python scripts, rather than through awkward `subprocess` calls that are causing trouble for conda environments with MPI support.

This merge also updates to the `compass` metapckage to 0.1.3:
 * updates `mpas_tools` to 0.1.10 (not yet released)
 * adds `rasterio`
 * adds `affine`
 * adds `ipython`
 * adds `jupyter`
  • Loading branch information
xylar authored Apr 9, 2020
2 parents 61aff5e + 25f05be commit 2957b79
Show file tree
Hide file tree
Showing 15 changed files with 1,089 additions and 752 deletions.
13 changes: 5 additions & 8 deletions compass/create_new_compass_env.bash
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ check_env () {
# Modify the following to choose which e3sm-unified version(s) the python version(s) are installed and whether to make
# an environment with x-windows support under cdat (cdatx) and/or without (nox). Typically, both environments should
# be created.
versions=(0.1.2)
versions=(0.1.3)
pythons=(3.7)
mpis=(serial mpich)

Expand All @@ -43,8 +43,8 @@ default_mpi=serial

# The rest of the script should not need to be modified
if [[ $HOSTNAME = "cori"* ]] || [[ $HOSTNAME = "dtn"* ]]; then
base_path="/global/cfs/cdirs/acme/software/anaconda_envs/base"
activ_path="/global/cfs/cdirs/acme/software/anaconda_envs"
base_path="/global/cfs/cdirs/e3sm/software/anaconda_envs/base"
activ_path="/global/cfs/cdirs/e3sm/software/anaconda_envs"
group="e3sm"
elif [[ $HOSTNAME = "acme1"* ]] || [[ $HOSTNAME = "aims4"* ]]; then
base_path="/usr/local/e3sm_unified/envs/base"
Expand All @@ -67,14 +67,11 @@ elif [[ $HOSTNAME = "compy"* ]]; then
base_path="/share/apps/E3SM/conda_envs/base"
activ_path="/share/apps/E3SM/conda_envs"
group="users"
elif [[ $HOSTNAME = "gr-fe"* ]] || [[ $HOSTNAME = "wf-fe"* ]]; then
elif [[ $HOSTNAME = "gr-fe"* ]] || [[ $HOSTNAME = "ba-fe"* ]]; then
base_path="/usr/projects/climate/SHARED_CLIMATE/anaconda_envs/base"
activ_path="/usr/projects/climate/SHARED_CLIMATE/anaconda_envs"
group="climate"
elif [[ $HOSTNAME = "burnham"* ]]; then
base_path="/home/xylar/Desktop/test_e3sm_unified/base"
activ_path="/home/xylar/Desktop/test_e3sm_unified"
group="xylar"
default_mpi=mpich
else
echo "Unknown host name $HOSTNAME. Add env_path and group for this machine to the script."
exit 1
Expand Down
12 changes: 8 additions & 4 deletions compass/meta.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{% set name = "compass" %}
{% set version = "0.1.2" %}
{% set build = 1 %}
{% set version = "0.1.3" %}
{% set build = 0 %}

{% if mpi == "nompi" %}
# prioritize nompi via build number
Expand Down Expand Up @@ -31,7 +31,7 @@ requirements:
run:
- python
- geometric_features 0.1.6 with_data*
- mpas_tools 0.0.9
- mpas_tools 0.0.10
- jigsaw 0.9.12
- jigsawpy 0.2.1
- metis
Expand All @@ -44,7 +44,11 @@ requirements:
- {{ mpi }} # [mpi != 'nompi']
- esmf * {{ mpi_prefix }}_*
- nco
- pyremap >=0.0.5,<0.1.0
- pyremap >=0.0.6,<0.1.0
- rasterio
- affine
- ipython
- jupyter

test:
imports:
Expand Down
23 changes: 23 additions & 0 deletions conda_package/docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ MPAS mesh tools

write_netcdf

.. currentmodule:: mpas_tools.scrip.from_mpas

.. autosummary::
:toctree: generated/

scrip_from_mpas

Ocean Tools
===========
Expand All @@ -66,3 +72,20 @@ Ocean Tools
add_critical_land_blockages
widen_transect_edge_masks

.. currentmodule:: mpas_tools.ocean.moc

.. autosummary::
:toctree: generated/

make_moc_basins_and_transects
build_moc_basins

Visualization
=============

.. currentmodule:: mpas_tools.viz.paraview_extractor

.. autosummary::
:toctree: generated/

extract_vtk
27 changes: 27 additions & 0 deletions conda_package/docs/authors.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
Main Authors
============
* Xylar Asay-Davis
* Michael Duda
* Matthew Hoffman
* Douglas Jacobsen

Contributors
============
* Riley X. Brady
* Miles Curry
* Amrapalli Garanaik
* Dom Heinzeller
* Trevor Hillebrand
* Joseph Kennedy
* William Lipscomb
* Mark Petersen
* Stephen Price
* Todd Ringler
* Juan Saenz
* Adrian Turner
* Luke Van Roekel
* Phillip J. Wolfram
* Tong Zhang

For a list of all the contributions:
https://github.com/MPAS-Dev/MPAS-Tools/graphs/contributors
31 changes: 29 additions & 2 deletions conda_package/docs/index.rst
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
mpas_tools
MPAS-Tools
==========

This repository houses geometric features relevant for climate science.
.. image:: _static/so60to10.png
:width: 500 px
:align: center

MPAS-Tools includes a python package, compiled Fortran, C and C++ tools, and
scripts for supporting initialization, visualization and analysis of Model for
Prediction Across Scales (MPAS) components. These tools are used by the
`COMPASS <https://github.com/MPAS-Dev/MPAS-Model/tree/master/testing_and_setup/compass>`_
(Configuring of MPAS Setups) framework within
`MPAS-Model <https://github.com/MPAS-Dev/MPAS-Model>`_ used to create
ocean and land-ice test cases,
the `MPAS-Analysis <https://github.com/MPAS-Dev/MPAS-Analysis>`_ package for
analyzing simulations, and in other MPAS-related workflows.

Developer's Guide
=================
.. toctree::
:maxdepth: 2

Expand All @@ -13,3 +27,16 @@ Indices and tables

* :ref:`genindex`

Authors
=======
.. toctree::
:maxdepth: 1

authors

Versions
========
.. toctree::
:maxdepth: 1

versions
11 changes: 11 additions & 0 deletions conda_package/docs/versions.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Versions
========

================ ===============
Documentation On GitHub
================ ===============
`stable`_ `master`_
================ ===============

.. _`stable`: ../stable/index.html
.. _`master`: https://github.com/MPAS-Dev/MPAS-Tools/tree/master
2 changes: 1 addition & 1 deletion conda_package/mpas_tools/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
__version_info__ = (0, 0, 9)
__version_info__ = (0, 0, 10)
__version__ = '.'.join(str(vi) for vi in __version_info__)
Empty file.
172 changes: 172 additions & 0 deletions conda_package/mpas_tools/scrip/from_mpas.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
# Create a SCRIP file from an MPAS mesh.
# See for details: http://www.earthsystemmodeling.org/esmf_releases/public/ESMF_5_2_0rp1/ESMF_refdoc/node3.html#SECTION03024000000000000000

import sys
import netCDF4
import numpy as np

from optparse import OptionParser


def scrip_from_mpas(mpasFile, scripFile, useLandIceMask=False):
"""
Create a SCRIP file from an MPAS mesh
Parameters
----------
mpasFile : str
The path to a NetCDF file with the MPAS mesh
scripFile : str
The path to the output SCRIP file
useLandIceMask : bool
Whether to use the landIceMask field for masking
"""
if useLandIceMask:
print(" -- Landice Masks are enabled")
else:
print(" -- Landice Masks are disabled")

# make a space in stdout before further output
print('')
fin = netCDF4.Dataset(mpasFile, 'r')
# This will clobber existing files
fout = netCDF4.Dataset(scripFile, 'w')

# Get info from input file
latCell = fin.variables['latCell'][:]
lonCell = fin.variables['lonCell'][:]
latVertex = fin.variables['latVertex'][:]
lonVertex = fin.variables['lonVertex'][:]
verticesOnCell = fin.variables['verticesOnCell'][:]
nEdgesOnCell = fin.variables['nEdgesOnCell'][:]
nCells = len(fin.dimensions['nCells'])
maxVertices = len(fin.dimensions['maxEdges'])
areaCell = fin.variables['areaCell'][:]
sphereRadius = float(fin.sphere_radius)
on_a_sphere = str(fin.on_a_sphere)

if sphereRadius <= 0:
print(" -- WARNING: conservative remapping is NOT possible when "
"'sphereRadius' <= 0 because 'grid_area' field will be infinite "
"(from division by 0)")

if on_a_sphere == "NO":
print(" -- WARNING: 'on_a_sphere' attribute is 'NO', which means that "
"there may be some disagreement regarding area between the "
"planar (source) and spherical (target) mesh")

if useLandIceMask:
landIceMask = fin.variables['landIceMask'][:]
else:
landIceMask = None

# Write to output file
# Dimensions
fout.createDimension("grid_size", nCells)
fout.createDimension("grid_corners", maxVertices)
fout.createDimension("grid_rank", 1)

# Variables
grid_center_lat = fout.createVariable('grid_center_lat', 'f8',
('grid_size',))
grid_center_lat.units = 'radians'
grid_center_lon = fout.createVariable('grid_center_lon', 'f8',
('grid_size',))
grid_center_lon.units = 'radians'
grid_corner_lat = fout.createVariable('grid_corner_lat', 'f8',
('grid_size', 'grid_corners'))
grid_corner_lat.units = 'radians'
grid_corner_lon = fout.createVariable('grid_corner_lon', 'f8',
('grid_size', 'grid_corners'))
grid_corner_lon.units = 'radians'
grid_area = fout.createVariable('grid_area', 'f8', ('grid_size',))
grid_area.units = 'radian^2'
grid_imask = fout.createVariable('grid_imask', 'i4', ('grid_size',))
grid_imask.units = 'unitless'
grid_dims = fout.createVariable('grid_dims', 'i4', ('grid_rank',))

grid_center_lat[:] = latCell[:]
grid_center_lon[:] = lonCell[:]
# SCRIP uses square radians
grid_area[:] = areaCell[:]/(sphereRadius**2)
grid_dims[:] = nCells

# grid corners:
# It is WAYYY faster to fill in the array entry-by-entry in memory than to
# disk.
grid_corner_lon_local = np.zeros((nCells, maxVertices))
grid_corner_lat_local = np.zeros((nCells, maxVertices))
for iCell in range(nCells):
vertexMax = nEdgesOnCell[iCell]
grid_corner_lat_local[iCell, 0:vertexMax] = \
latVertex[verticesOnCell[iCell, 0:vertexMax] - 1]
grid_corner_lon_local[iCell, 0:vertexMax] = \
lonVertex[verticesOnCell[iCell, 0:vertexMax] - 1]
if vertexMax < maxVertices:
# repeat the last vertex location for any remaining, unused vertex
# indices
grid_corner_lat_local[iCell, vertexMax:] = \
latVertex[verticesOnCell[iCell, vertexMax-1] - 1]
grid_corner_lon_local[iCell, vertexMax:] = \
lonVertex[verticesOnCell[iCell, vertexMax-1] - 1]

if useLandIceMask:
# If useLandIceMask are enabled, mask out ocean under land ice.
grid_imask[iCell] = 1 - landIceMask[0, iCell]
else:
# If landiceMasks are not enabled, don't mask anything out.
grid_imask[iCell] = 1
grid_corner_lat[:] = grid_corner_lat_local[:]
grid_corner_lon[:] = grid_corner_lon_local[:]

print("Input latCell min/max values (radians): {}, {}".format(
latCell[:].min(), latCell[:].max()))
print("Input lonCell min/max values (radians): {}, {}".format(
lonCell[:].min(), lonCell[:].max()))
print("Calculated grid_center_lat min/max values (radians): {}, {}".format(
grid_center_lat[:].min(), grid_center_lat[:].max()))
print("Calculated grid_center_lon min/max values (radians): {}, {}".format(
grid_center_lon[:].min(), grid_center_lon[:].max()))
print("Calculated grid_area min/max values (sq radians): {}, {}".format(
grid_area[:].min(), grid_area[:].max()))

fin.close()
fout.close()

print("Creation of SCRIP file is complete.")


def main():
print("== Gathering information. (Invoke with --help for more details. All"
" arguments are optional)")
parser = OptionParser()
parser.description = "This script takes an MPAS grid file and generates " \
"a SCRIP grid file."
parser.add_option("-m", "--mpas", dest="mpasFile",
help="MPAS grid file name used as input.",
default="grid.nc", metavar="FILENAME")
parser.add_option("-s", "--scrip", dest="scripFile",
help="SCRIP grid file to output.", default="scrip.nc",
metavar="FILENAME")
parser.add_option("-l", "--landice", dest="landiceMasks",
help="If flag is on, landice masks will be computed "
"and used.",
action="store_true")
for option in parser.option_list:
if option.default != ("NO", "DEFAULT"):
option.help += (" " if option.help else "") + "[default: %default]"
options, args = parser.parse_args()

if not options.mpasFile:
sys.exit('Error: MPAS input grid file is required. Specify with -m '
'command line argument.')
if not options.scripFile:
sys.exit('Error: SCRIP output grid file is required. Specify with -s '
'command line argument.')

if not options.landiceMasks:
options.landiceMasks = False

scrip_from_mpas(options.mpasFile, options.scripFile, options.landiceMasks)
Empty file.
Loading

0 comments on commit 2957b79

Please sign in to comment.