Source code for mpas_analysis.ocean.geojson_netcdf_transects

# This software is open source software available under the BSD-3 license.
#
# Copyright (c) 2022 Triad National Security, LLC. All rights reserved.
# Copyright (c) 2022 Lawrence Livermore National Security, LLC. All rights
# reserved.
# Copyright (c) 2022 UT-Battelle, LLC. All rights reserved.
#
# Additional copyright and license information can be found in the LICENSE file
# distributed with this code, or at
# https://raw.githubusercontent.com/MPAS-Dev/MPAS-Analysis/main/LICENSE
import json
from pathlib import Path

import numpy as np
import xarray as xr

from mpas_analysis.ocean.compute_transects_subtask import (
    ComputeTransectsSubtask,
    TransectsObservations
)
from mpas_analysis.ocean.plot_transect_subtask import PlotTransectSubtask
from mpas_analysis.shared import AnalysisTask


[docs] class GeojsonNetcdfTransects(AnalysisTask): """ Plot model output at transects defined by lat/lon points in a geojson or NetCDF file """ # Authors # ------- # Xylar Asay-Davis
[docs] def __init__(self, config, mpasClimatologyTask, controlConfig=None): """ Construct the analysis task and adds it as a subtask of the ``parentTask``. Parameters ---------- config : mpas_tools.config.MpasConfigParser Configuration options mpasClimatologyTask : ``MpasClimatologyTask`` The task that produced the climatology to be remapped and plotted as a transect controlconfig : mpas_tools.config.MpasConfigParser, optional Configuration options for a control run (if any) """ # Authors # ------- # Xylar Asay-Davis tags = ['climatology', 'transect', 'geojson', 'netcdf'] # call the constructor from the base class (AnalysisTask) super().__init__( config=config, taskName='geojsonNetcdfTransects', componentName='ocean', tags=tags) sectionName = self.taskName geojsonOrNetcdfFiles = config.getexpression(sectionName, 'geojsonOrNetcdfFiles') if len(geojsonOrNetcdfFiles) == 0: return seasons = config.getexpression(sectionName, 'seasons') horizontalResolution = config.get(sectionName, 'horizontalResolution') verticalComparisonGridName = config.get(sectionName, 'verticalComparisonGridName') if verticalComparisonGridName in ['mpas', 'obs']: verticalComparisonGrid = None else: verticalComparisonGrid = config.getexpression( sectionName, 'verticalComparisonGrid', use_numpyfunc=True) availableVariables = config.getexpression( sectionName, 'availableVariables') prefixes = config.getexpression(sectionName, 'variables') fields = [field for field in availableVariables if field['prefix'] in prefixes] geojsonFileNames = {} netcdfFileNames = {} transectNames = [] for fileName in geojsonOrNetcdfFiles: ext = Path(fileName).suffix if ext == '.nc': transectName = Path(fileName).stem netcdfFileNames[transectName] = fileName elif ext == '.geojson': with open(fileName) as filePointer: jsonFile = json.load(filePointer) for feature in jsonFile['features']: if feature['geometry']['type'] != 'LineString': continue transectName = feature['properties']['name'] geojsonFileNames[transectName] = fileName else: raise ValueError(f'Unexptect file extension: {ext}') if transectName in transectNames: raise ValueError(f'Transect name {transectName} is repeated.') transectNames.append(transectName) variableList = [field['mpas'] for field in fields] computeGeojsonTransectsSubtask = None computeNetcdfTransectsSubtask = None if geojsonFileNames: transectCollectionName = 'geojson_transects' if horizontalResolution != 'obs' and \ horizontalResolution != 'mpas': transectCollectionName = \ f'{transectCollectionName}_{horizontalResolution}km' geojsonObservations = GeojsonTransectsObservations( config, geojsonFileNames, horizontalResolution, transectCollectionName) computeGeojsonTransectsSubtask = ComputeTransectsSubtask( mpasClimatologyTask=mpasClimatologyTask, parentTask=self, climatologyName='geojson', transectCollectionName=transectCollectionName, variableList=variableList, seasons=seasons, obsDatasets=geojsonObservations, verticalComparisonGridName=verticalComparisonGridName, verticalComparisonGrid=verticalComparisonGrid, subtaskName='remapGeojson') if netcdfFileNames: transectCollectionName = 'netcdf_transects' if horizontalResolution != 'obs' and \ horizontalResolution != 'mpas': transectCollectionName = \ f'{transectCollectionName}_{horizontalResolution}km' netcdfObservations = NetcdfTransectsObservations( config, netcdfFileNames, horizontalResolution, transectCollectionName) computeNetcdfTransectsSubtask = ComputeTransectsSubtask( mpasClimatologyTask=mpasClimatologyTask, parentTask=self, climatologyName='netcdf', transectCollectionName=transectCollectionName, variableList=variableList, seasons=seasons, obsDatasets=netcdfObservations, verticalComparisonGridName=verticalComparisonGridName, verticalComparisonGrid=verticalComparisonGrid, subtaskName='remapNetcdf') for field in fields: for transectName in geojsonFileNames: for season in seasons: self._add_plot_subtasks( field=field, season=season, transectName=transectName, computeSubtask=computeGeojsonTransectsSubtask, galleryGroup='Geojson Transects', groupLink='geojson', controlConfig=controlConfig) for transectName in netcdfFileNames: for season in seasons: self._add_plot_subtasks( field=field, season=season, transectName=transectName, computeSubtask=computeNetcdfTransectsSubtask, galleryGroup='NetCDF Transects', groupLink='nctransects', controlConfig=controlConfig)
def _add_plot_subtasks(self, field, season, transectName, computeSubtask, galleryGroup, groupLink, controlConfig): """ Add a sbutask for plotting the given transect, field and season """ config = self.config sectionName = self.taskName verticalBounds = config.getexpression(sectionName, 'verticalBounds') fieldPrefix = field['prefix'] outFileLabel = fieldPrefix if controlConfig is None: refFieldName = None refTitleLabel = None diffTitleLabel = None else: refFieldName = field['mpas'] controlRunName = controlConfig.get('runs', 'mainRunName') refTitleLabel = f'Control: {controlRunName}' diffTitleLabel = 'Main - Control' fieldPrefixUpper = fieldPrefix[0].upper() + fieldPrefix[1:] fieldNameInTitle = field['titleName'] transectNameInTitle = transectName.replace('_', ' ') fieldNameInTitle = \ f'{fieldNameInTitle} from {transectNameInTitle}' configSectionName = f'geojsonNetcdf{fieldPrefixUpper}Transects' # make a new subtask for this season and comparison grid subtask = PlotTransectSubtask( parentTask=self, season=season, transectName=transectName, fieldName=fieldPrefix, computeTransectsSubtask=computeSubtask, plotObs=False, controlConfig=controlConfig) subtask.set_plot_info( outFileLabel=outFileLabel, fieldNameInTitle=fieldNameInTitle, mpasFieldName=field['mpas'], refFieldName=refFieldName, refTitleLabel=refTitleLabel, diffTitleLabel=diffTitleLabel, unitsLabel=field['units'], imageCaption=fieldNameInTitle, galleryGroup=galleryGroup, groupSubtitle=None, groupLink=groupLink, galleryName=field['titleName'], configSectionName=configSectionName, verticalBounds=verticalBounds) self.add_subtask(subtask)
class GeojsonTransectsObservations(TransectsObservations): """ A class for loading and manipulating geojson transects """ # Authors # ------- # Xylar Asay-Davis def build_observational_dataset(self, fileName, transectName): """ read in the data sets for observations, and possibly rename some variables and dimensions Parameters ---------- fileName : str observation file name transectName : str transect name Returns ------- dsObs : ``xarray.Dataset`` The observational dataset """ # Authors # ------- # Xylar Asay-Davis with open(fileName) as filePointer: jsonFile = json.load(filePointer) for feature in jsonFile['features']: if feature['properties']['name'] != transectName: continue assert feature['geometry']['type'] == 'LineString' coordinates = feature['geometry']['coordinates'] lon, lat = zip(*coordinates) break dsObs = xr.Dataset() dsObs['lon'] = (('nPoints',), np.array(lon)) dsObs.lon.attrs['units'] = 'degrees' dsObs['lat'] = (('nPoints',), np.array(lat)) dsObs.lat.attrs['units'] = 'degrees' return dsObs class NetcdfTransectsObservations(TransectsObservations): """ A class for loading and manipulating netcdf transects """ # Authors # ------- # Xylar Asay-Davis def build_observational_dataset(self, fileName, transectName): """ read in the data sets for observations, and possibly rename some variables and dimensions Parameters ---------- fileName : str observation file name transectName : str transect name Returns ------- dsObs : ``xarray.Dataset`` The observational dataset """ # Authors # ------- # Xylar Asay-Davis dsObs = xr.open_dataset(fileName) # drop all variables besides lon and lat dsObs = dsObs[['lon', 'lat']] return dsObs