Skip to content

Commit

Permalink
Alpha release 3.1.0
Browse files Browse the repository at this point in the history
1. Bug fix for relative paths
2. Added opendap functionality
3. Added ensemble hasnan and naming
  • Loading branch information
JonKing93 committed Nov 3, 2020
1 parent 2d9a3f8 commit 096e243
Show file tree
Hide file tree
Showing 25 changed files with 456 additions and 261 deletions.
4 changes: 2 additions & 2 deletions @dash/checkStrsInList.m
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@
% Informative error message
badName = name;
if numel(input)>1
badName = sprintf('Element %.f in %s (%s)', bad, name, input(bad));
badName = sprintf('Element %.f in %s', bad, name);
end
error('%s is not a %s. Allowed values are %s.', badName, listName, dash.messageList(list));
error('%s (%s) is not a %s. Allowed values are %s.', badName, input(bad), listName, dash.messageList(list));
end

end
18 changes: 16 additions & 2 deletions @ensemble/ensemble.m
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@

properties (SetAccess = private)
file; % The .ens file associated with the object
name; % The name of the ensemble object

hasnan; % Whether a variable has NaN in an ensemble member
has_nan; % Whether a variable has NaN in an ensemble member
metadata; % Ensemble metadata object for the saved state vector ensemble
stateVector; % The stateVector object used to build the ensemble

Expand All @@ -26,7 +27,7 @@

% Constructor
methods
function obj = ensemble(filename)
function obj = ensemble(filename, name)
%% Creates a new ensemble object
%
% obj = ensemble(filename)
Expand All @@ -37,12 +38,17 @@
% Returns an ensemble object for a .ens file with the specified
% full file path.
%
% obj = ensemble(filename, name)
% Provides an identifying name for the ensemble object.
%
% ----- Inputs -----
%
% filename: The name of a .ens file on the active path. A string.
%
% fullname: The full file path to a .ens file. A string.
%
% name: An identifying name for the ensemble object. A string.
%
% ----- Outputs -----
%
% obj: An ensemble object for the specified .ens file.
Expand All @@ -55,6 +61,12 @@
% Members and variables are unspecified.
obj.members = [];
obj.variables = [];

% Update name (error checking via ensembleMetadata)
if exist('name','var')
obj.metadata = obj.metadata.rename(name);
obj.name = name;
end
end
end

Expand All @@ -75,5 +87,7 @@
obj = useVariables(obj, variables);
varNames = variableNames(obj);
s = info(obj);
nanMembers = hasnan(obj, varNames);
obj = rename(obj, newName);
end
end
46 changes: 46 additions & 0 deletions @ensemble/hasnan.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
function[nanMembers] = hasnan(obj, varNames)
%% Indicates whether ensemble members contain NaN elements for variables
% in a state vector ensemble.
%
% nanMembers = obj.hasnan
% Returns a row vector that indicates which ensemble members contain NaN
% elements.
%
% nanVars = obj.hasnan(varNames)
% Returns a logical matrix that indicates whether specified variables have
% NaN elements in ensemble members.
%
% nanVars = obj.hasnan([])
% Indicates whether variables have NaN elements in ensemble members for
% each variable in a state vector.
%
% ----- Inputs -----
%
% varNames: A list of variables in a state vector. A string vector or
% cellstring vector.
%
% ----- Outputs -----
%
% nanMembers: A logical row vector with one element per ensemble member. True
% elements indicate that the ensemble member contains NaN elements.
%
% nanVars: A logical matrix with one column per ensemble member. Each row
% indicates whether a particular variable has NaN elements in each
% ensemble member.

% If no inputs, return summary for all variables
if ~exist('varNames','var')
nanMembers = any(obj.has_nan,1);
return;
end

% Otherwise, return for specified variables
allVars = obj.metadata.variableNames;
if isempty(varNames)
v = 1:numel(allVars);
else
v = dash.checkStrsInList(varNames, allVars, 'varNames', 'variable in the state vector');
end
nanMembers = obj.has_nan(v,:);

end
18 changes: 18 additions & 0 deletions @ensemble/rename.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
function[obj] = rename(obj, newName)
%% Renames an ensemble object
%
% obj = obj.rename(newName)
%
% ----- Inputs -----
%
% newName: The new name for the ensemble object. A string.
%
% ----- Outputs -----
%
% obj: The updated ensemble object

% Update name (error checking via ensembleMetadata)
obj.metadata = obj.metadata.rename(name);
obj.name = dash.assertStrFlag(newName, 'newName');

end
4 changes: 3 additions & 1 deletion @ensemble/update.m
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,16 @@

% If no matfile object is provided, load data into a structure
fields = ["hasnan","metadata","stateVector"];
props = ["has_nan", "metadata", "stateVector"];
if ~exist('ens','var') || isempty(ens)
ens = dash.loadMatfileFields(obj.file, fields, '.ens');
end

% Fill in the fields
for f = 1:numel(fields)
name = char(fields(f));
obj.(name) = ens.(name);
propName = char(props(f));
obj.(propName) = ens.(name);
end

end
11 changes: 8 additions & 3 deletions @ensemble/useMembers.m
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,13 @@

% Update. Error check the ensemble members. Save
obj = obj.update;
[~, nEns] = obj.metadata.sizes;
members = dash.checkIndices(members, 'members', nEns, 'the number of ensemble members');
obj.members = members(:);
if ~exist('members','var') || isempty(members)
members = [];
else
[~, nEns] = obj.metadata.sizes;
members = dash.checkIndices(members, 'members', nEns, 'the number of ensemble members');
members = members(:);
end
obj.members = members;

end
11 changes: 8 additions & 3 deletions @ensemble/useVariables.m
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,13 @@

% Check variables. Update. Save
obj = obj.update;
obj.stateVector.checkVariables(varNames);
varNames = string(varNames);
obj.variables = unique(varNames(:));
if ~exist('varNames','var') || isempty(varNames)
varNames = [];
else
obj.stateVector.checkVariables(varNames);
varNames = string(varNames);
varNames = unique(varNames(:));
end
obj.variables = varNames;

end
23 changes: 17 additions & 6 deletions @ensembleMetadata/ensembleMetadata.m
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
% Jonathan King, University of Arizona, 2019-2020

properties (SetAccess = private)
ensembleName; % Name of the ensemble
name; % Name of the ensemble
vectorName; % Name of the state vector template

variableNames; % Names of each metadata
Expand All @@ -51,27 +51,36 @@

% Constructor
methods
function obj = ensembleMetadata(sv)
%% Returns an ensembleMetadata object for a stateVector, ensemble, or .ens file.
function obj = ensembleMetadata(sv, name)
%% Returns an ensembleMetadata object for a stateVector.
%
% obj = ensembleMetadata(sv)
% Creates an ensembleMetadata object for a state vector.
%
% obj = ensembleMetadata(sv, name)
% Optionally gives an identifying name for the ensemble.
%
% ----- Inputs -----
%
% sv: A stateVector object
%
% name: An identifying name for the ensemble. A string
%
% ----- Outputs -----
%
% meta: The ensemble metadata object
% obj: The ensemble metadata object

% Error check.
if ~isa(sv, 'stateVector') || ~isscalar(sv)
error('sv must be a scalar stateVector object.');
end

% Get names and size
obj.ensembleName = [];
if exist('name','var')
obj.name = dash.assertStrFlag(name, 'name');
else
obj.name = [];
end
obj.vectorName = sv.name;

obj.nEns = 0;
Expand Down Expand Up @@ -151,6 +160,8 @@
% User methods
methods
% variableNames -- Just a direct call to the field
obj = rename(obj, newName);

[V, meta] = regrid(obj, X, varName, dimOrder, d, keepSingletons);
meta = variable(obj, varName, dims, type, indices);
meta = dimension(obj, dim, alwaysStruct);
Expand All @@ -166,5 +177,5 @@
obj = appendMembers(obj, meta2);
obj = extractMembers(obj, members);
end

end
16 changes: 16 additions & 0 deletions @ensembleMetadata/rename.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
function[obj] = rename(obj, newName)
%% Changes the identifying ensemble name for the ensembleMetadata object.
%
% obj = obj.rename(newName)
%
% ----- Inputs -----
%
% newName: The new identifying name for the ensemble. A string.
%
% ----- Outputs -----
%
% obj: The updated ensembleMetadata object.

obj.name = dash.assertStrFlag(newName, 'newName');

end
30 changes: 18 additions & 12 deletions @gridfile/add.m
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
function[] = add( obj, type, file, var, dims, meta, varargin )
% Adds a data source to a .grid file.
%
% obj.add( type, file, var, dims, meta )
% Add a data source. Notes the type of data source (NetCDF vs .mat), the
% name of the file, the name of the data variable in the file, the order of
% the dimensions for the variable, and the metadata associated with each
% dimension.
% obj.add('nc', file, var, dims, meta)
% Adds a NetCDF data source.
%
% obj.add('mat', file, var, dims, meta)
% Adds a .mat data source.
%
% obj.add('opendap', url, var, dims, meta)
% Adds an OPeNDAP data source.
%
% obj.add( ..., 'fill', fill )
% Specifies a fill value for the data source. When data is loaded from the
Expand Down Expand Up @@ -38,6 +41,8 @@
% path. Use the full file path to add a file off the active path. All
% file names must include the file extension.
%
% url: An OPeNDAP url. A string.
%
% var: The name of the variable in the source file.
%
% dims: The order of the dimensions of the variable in the source file. A
Expand Down Expand Up @@ -66,7 +71,8 @@
%
% absolute: A scalar logical indicating whether to save data source file
% names as an absolute path (true), or as a path relative to the .grid
% file (false). Default is false.
% file (false). Default is false. Files located on a different drive or
% via an OPeNDAP url will use an absolute path.

% Update the gridfile object in case the file was changed.
obj.update;
Expand Down Expand Up @@ -131,13 +137,13 @@
% The source metadata must exactly match a sequence of .grid metadata
[inGrid, order] = ismember(value, obj.meta.(metaDims(d)), 'rows');
if any(~inGrid)
error('The %s metadata in row %.f of data source %s does not match any %s metadata in .grid file %s.', metaDims(d), find(~inGrid,1), source.file, metaDims(d), obj.file);
error('The %s metadata in row %.f of data source %s does not match any %s metadata in .grid file %s.', metaDims(d), find(~inGrid,1), source.source, metaDims(d), obj.file);
elseif nRows>1 && issorted(order, 'strictdescend')
error('The %s metadata for data source %s is in the opposite order of the %s metadata in .grid file %s.', metaDims(d), source.file, metaDims(d), obj.file );
error('The %s metadata for data source %s is in the opposite order of the %s metadata in .grid file %s.', metaDims(d), source.source, metaDims(d), obj.file );
elseif ~issorted(order, 'strictascend')
error('The %s metadata for data source %s is in a different order than the %s metadata in .grid file %s.', metaDims(d), source.file, metaDims(d), obj.file );
error('The %s metadata for data source %s is in a different order than the %s metadata in .grid file %s.', metaDims(d), source.source, metaDims(d), obj.file );
elseif nRows>1 && ~isequal(unique(diff(order)), 1)
error('The %s metadata for data source %s skips elements that are in the %s metadata for .grid file %s.', metaDims(d), source.file, metaDims(d), obj.file );
error('The %s metadata for data source %s skips elements that are in the %s metadata for .grid file %s.', metaDims(d), source.source, metaDims(d), obj.file );
end

% Record the limits of the source data dimensions in the .grid file
Expand All @@ -151,13 +157,13 @@
higher = all(dimLimit>obj.dimLimit(:,2,:), 2);
overlap = all(~(lower|higher), 1);
if any(overlap)
error('The data in new source file %s overlaps data in file %s, which is already in .grid file %s.', source.file, obj.source.file(find(overlap,1),:), obj.file);
error('The data in new source %s overlaps data source %s, which is already in .grid file %s.', source.source, obj.source.source(find(overlap,1),:), obj.file);
end

% Convert the dataSource object into a structure of primitives and
% implement the desired filepath style
source = gridfile.convertSourceToPrimitives(source);
source.file = obj.sourceFilepath(source.file, absolute);
source.source = obj.sourceFilepath(source.source, absolute);

% Preallocate the length of each of the primitive fields
sourceFields = fields(obj.source);
Expand Down
2 changes: 1 addition & 1 deletion @gridfile/buildSourcesForFiles.m
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
% Provide extra error information if the data source file is missing
catch ME
if strcmp(ME.identifier, "DASH:missingFile")
error('Cannot find data source file "%s". It may have been moved, renamed, or deleted. If the file was moved or renamed, see "gridfile.renameSources" to update the data source file path.', filenames(s));
error('Cannot find data source "%s". It may have been moved, renamed, or deleted. If the file was moved or renamed, see "gridfile.renameSources" to update the data source file path.', filenames(s));
end
rethrow(ME);
end
Expand Down
7 changes: 5 additions & 2 deletions @gridfile/collectFullPaths.m
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,13 @@
% paths: A string vector of absolute file paths.

% Get the file names
paths = obj.collectPrimitives("file", s);
paths = obj.collectPrimitives("source", s);

% Get the .grid file folders
gridPath = fileparts(obj.file);
gridFolders = split(gridPath, filesep);

% Append the .grid file path to relative paths
gridFolders = split(obj.file, filesep);
for f = 1:numel(paths)
file = char(paths(f));
if file(1)=='.'
Expand Down
4 changes: 3 additions & 1 deletion @gridfile/convertSourceToPrimitives.m
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
s = struct();

% Convert strings to chars
s.file = char(source.file);
s.source = char(source.source);
s.var = char(source.var);
s.dataType = char(source.dataType);

Expand All @@ -37,6 +37,8 @@
s.type = 'nc';
elseif isa(source, 'matSource')
s.type = 'mat';
elseif isa(source, 'opendapSource')
s.type = 'opendap';
end

% Post-processing fields
Expand Down
Loading

0 comments on commit 096e243

Please sign in to comment.