-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
59 changed files
with
2,206 additions
and
241 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +0,0 @@ | ||
[submodule "4. PSMs/Specific Forward Models/MgCa/BAYMAG"] | ||
path = 4. PSMs/Specific Forward Models/MgCa/BAYMAG | ||
url = https://github.com/jesstierney/BAYMAG | ||
[submodule "4. PSMs/Specific Forward Models/UK 37/BAYSPLINE"] | ||
path = 4. PSMs/Specific Forward Models/UK 37/BAYSPLINE | ||
url = [email protected]:jesstierney/BAYSPLINE | ||
[submodule "PSMs/Submodules"] | ||
path = PSMs/Submodules | ||
url = https://github.com/jesstierney/BAYMAG | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
classdef (Abstract) PSM | ||
%% Implements a common interface for all PSMs | ||
% | ||
% PSM Methods: | ||
% estimate - Estimate proxy values from a state vector ensemble | ||
% rename - Renames a PSM | ||
|
||
properties (SetAccess = private) | ||
name; | ||
rows; | ||
estimatesR; | ||
end | ||
|
||
methods | ||
% Constructor | ||
function[obj] = PSM(name, estimatesR) | ||
%% PSM constructor. Records a name and whether the PSM can estimate R | ||
% | ||
% obj = PSM | ||
% Does not specify a name. Does not estimate R. | ||
% | ||
% obj = PSM(name) | ||
% Creates a PSM with a particular name. Does not estimate R. | ||
% | ||
% obj = PSM(name, estimatesR) | ||
% Creates a PSM with a name and specifies whether the PSM can | ||
% estimate R. | ||
% | ||
% ----- Inputs ----- | ||
% | ||
% name: A name for the PSM. A string. | ||
% | ||
% estimatesR: A scalar logical indicating whether the PSM can | ||
% estimate R (true), or not (false -- defalt) | ||
% | ||
% ----- Outputs ----- | ||
% | ||
% obj: The new PSM object | ||
|
||
% Note if PSM can estimate R | ||
if ~exist('estimatesR', 'var') || isempty(estimatesR) | ||
estimatesR = false; | ||
end | ||
obj = obj.canEstimateR(estimatesR); | ||
|
||
% Optionally record a name | ||
if ~exist('name','var') || isempty(name) | ||
name = ""; | ||
end | ||
obj = obj.rename(name); | ||
end | ||
|
||
% Rename | ||
function[obj] = rename(obj, name) | ||
%% Renames a PSM | ||
% | ||
% obj = obj.rename(newName) | ||
% | ||
% ----- Inputs ----- | ||
% | ||
% newName: A new name for the PSM. A string | ||
% | ||
% ----- Outputs ----- | ||
% | ||
% obj: The renamed PSM | ||
|
||
obj.name = dash.assertStrFlag(name, 'name'); | ||
end | ||
|
||
% Return name for error messages | ||
function[name] = messageName(obj, n) | ||
%% Returns an identifying name for the PSM for use in messages | ||
% | ||
% name = obj.messageName | ||
% Returns a name for the PSM. | ||
% | ||
% name = obj.messageName(n) | ||
% Returns a name for the PSM and identifies its position in a list | ||
% | ||
% ----- Inputs ----- | ||
% | ||
% n: A list index. A scalar, positive integer | ||
% | ||
% ----- Outputs ----- | ||
% | ||
% name: A string identifying the PSM | ||
|
||
% Get some settings | ||
hasNumber = exist('n', 'var'); | ||
hasName = ~strcmp(obj.name, ""); | ||
|
||
% Create the message string | ||
name = "PSM"; | ||
if hasNumber | ||
name = strcat(name, sprintf(' %.f', n)); | ||
if hasName | ||
name = strcat(name, sprintf(' ("%s")', obj.name)); | ||
end | ||
elseif hasName | ||
name = strcat(name, sprintf(' "%s"', obj.name)); | ||
end | ||
end | ||
|
||
% Set the rows | ||
function[obj] = useRows(obj, rows) | ||
%% Specifies the state vector rows used by a PSM | ||
% | ||
% obj = obj.useRows(rows) | ||
% | ||
% ----- Inputs ----- | ||
% | ||
% rows: The rows used by the PSM. A vector of positive integers. | ||
% | ||
% ----- Outputs ----- | ||
% | ||
% obj: The updated PSM object | ||
|
||
% Error check | ||
assert(isvector(rows), 'rows must be a vector'); | ||
assert(islogical(rows) || isnumeric(rows), 'rows must be numeric or logical'); | ||
|
||
% Error check numeric indices | ||
if isnumeric(rows) | ||
dash.assertPositiveIntegers(rows, 'rows'); | ||
|
||
% Convert logical to numeric | ||
else | ||
rows = find(rows); | ||
end | ||
|
||
% Save | ||
obj.rows = rows(:); | ||
end | ||
|
||
% Allow R to be estimated | ||
function[obj] = canEstimateR(obj, tf) | ||
%% Specifies whether a PSM can estimate R | ||
% | ||
% obj = obj.canEstimateR(tf) | ||
% | ||
% ----- Inputs ----- | ||
% | ||
% tf: A scalar logical indicating if the PSM can estimate R | ||
% (true) or not (false) | ||
% | ||
% ----- Outputs ----- | ||
% | ||
% obj: The updated PSM object | ||
|
||
dash.assertScalarType(tf, 'tf', 'logical', 'logical'); | ||
obj.estimatesR = tf; | ||
end | ||
end | ||
|
||
methods (Static) | ||
% Estimate Y values for a set of PSMs | ||
[Ye, R] = estimate(X, F) | ||
|
||
% Download the code for a PSM | ||
download(psmName, path); | ||
|
||
% Get the repository and commit information for a PSM | ||
[repo, commit] = githubLocation(psmName); | ||
end | ||
|
||
% Run individual PSM objects | ||
methods (Abstract) | ||
[Y, R] = runPSM(obj, X); | ||
end | ||
|
||
% Run PSMs directly | ||
methods (Abstract, Static) | ||
[Y, R] = run(varargin); | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
function[] = download(psmName, path) | ||
%% Loads the code for a PSM and adds it to the active path. | ||
% | ||
% PSM.download(psmName) | ||
% Downloads the code for a PSM to the current directory and adds it to the | ||
% active path. | ||
% | ||
% PSM.download(psmName, path) | ||
% Downloads the code for a PSM to the specified directory and adds it to the | ||
% active path. The specified directory must be empty | ||
% | ||
% ----- Inputs ----- | ||
% | ||
% psmName: The name of a PSM to download. A string. Options are | ||
% 'bayspar': The BAYSPAR TEX86 PSM | ||
% 'bayspline': The BAYSPLINE UK37 PSM | ||
% 'baymag': The BAYMAG Mg/Ca PSM | ||
% | ||
% path: Indicates the folder where the PSM code should be downloaded. A | ||
% string. | ||
|
||
% Default and error check the path | ||
if ~exist('path','var') || isempty(path) | ||
path = pwd; | ||
userPath = false; | ||
else | ||
userPath = true; | ||
path = dash.assertStrFlag(path); | ||
end | ||
|
||
% Get the Github repository for the PSM | ||
[repo, commit] = PSM.githubLocation(psmName); | ||
|
||
% Get the final download path. Ensure the folder is empty if it exists | ||
if ~userPath | ||
[~, defaultFolder] = fileparts(repo); | ||
path = fullfile(path, defaultFolder); | ||
end | ||
if isfolder(path) | ||
contents = dir(path); | ||
if ~strcmp([contents.name], "...") | ||
error('The folder "%s" is not empty. You can only download PSM code to a new or empty folder.', path); | ||
end | ||
end | ||
|
||
% Clone the repository | ||
clone = sprintf("git clone %s %s", repo, path); | ||
status = system(clone); | ||
if status~=0 | ||
gitFailureError(repo, commit); | ||
end | ||
|
||
% Checkout the commit | ||
home = pwd; | ||
try | ||
cd(path); | ||
checkout = sprintf( "git checkout %s -q", commit ); | ||
status = system(checkout); | ||
assert(status==0); | ||
|
||
% Delete the clone and return to the working directory if checkout fails | ||
catch | ||
cd(home); | ||
rmdir(path, 's'); | ||
gitFailureError(repo, commit); | ||
end | ||
|
||
% Add PSM code to path and return to working directory | ||
cd(home); | ||
addpath(genpath(path)); | ||
|
||
end | ||
|
||
% Long error message | ||
function[] = gitFailureError(repo, commit) | ||
github = strcat(repo, '/tree/', commit); | ||
error(['Could not download the repository. Please check that you have ',... | ||
'git installed and on your system path. If problems persist, you ',... | ||
'can download the PSM code manually at "%s".'], github); | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
function[Ye, R] = estimate(X, F) | ||
%% Estimates proxies values from a state vector ensemble | ||
% | ||
% Ye = PSM.estimate(X, psms) | ||
% Estimates proxies values for a state vector ensemble given a | ||
% set of PSMs. | ||
% | ||
% [Ye, R] = PSM.estimates(X, psms) | ||
% Also estimates proxy uncertainties (R). | ||
% | ||
% ----- Inputs ----- | ||
% | ||
% X: A state vector ensemble. A numeric array with the | ||
% following dimensions (State vector x ensemble members x priors) | ||
% | ||
% psms: A set of PSMs. Either a scalar PSM object or a cell | ||
% vector whose elements are PSM objects. | ||
% | ||
% ----- Outputs ----- | ||
% | ||
% Ye: Proxy estimates. A numeric array with the dimensions | ||
% (Proxy sites x ensemble members x priors) | ||
% | ||
% R: Proxy uncertainty estimates. A numeric array with | ||
% dimensions (proxy sites x ensemble members x priors) | ||
|
||
% Parse and error check the ensemble. Get size | ||
if isa(X, 'ensemble') | ||
isens = true; | ||
assert(isscalar(X), 'ens must be a scalar ensemble object'); | ||
[nState, nEns] = X.metadata.sizes; | ||
nPriors = 1; | ||
m = matfile(X.file); | ||
else | ||
assert(isnumeric(X), 'X must be numeric'); | ||
assert(ndims(X)<=3, 'X cannot have more than 3 dimensions'); | ||
isens = false; | ||
[nState, nEns, nPriors] = size(X); | ||
end | ||
|
||
% Error check the ensemble, get sizes | ||
dash.assertRealDefined(X, 'X'); | ||
|
||
% Parse the PSM vector | ||
nSite = numel(F); | ||
[F, wasCell] = dash.parseInputCell(F, nSite, 'F'); | ||
name = "F"; | ||
|
||
% Error check the individual PSMs | ||
for s = 1:nSite | ||
if wasCell | ||
name = sprintf('Element %.f of F', s); | ||
end | ||
dash.assertScalarType(F{s}, name, 'PSM', 'PSM'); | ||
|
||
% Check the rows of the PSM do not exceed the number of rows | ||
if max(F{s}.rows) > nState | ||
error('The ensemble has %.f rows, but %s uses rows that are larger (%.f)', ... | ||
nState, F{s}.messageName(s), max(F{s}.rows)); | ||
end | ||
end | ||
|
||
% Preallocate | ||
Ye = NaN(nSite, nEns, nPriors); | ||
if nargout>1 | ||
R = NaN(nSite, nEns, nPriors); | ||
end | ||
|
||
% Get the values needed to run each PSM | ||
for s = 1:nSite | ||
if ~isens | ||
Xpsm = X(F{s}.rows,:,:); | ||
|
||
% If using an ensemble object, first attempt to read all rows at once | ||
else | ||
try | ||
rows = dash.equallySpacedIndices(F{s}.rows); | ||
Xpsm = m.X(rows,:); | ||
[~, keep] = ismember(F{s}.rows, rows); | ||
Xpsm = Xpsm(keep,:); | ||
|
||
% If unsuccessful, load values iteratively | ||
catch | ||
nRows = numel(F{s}.rows); | ||
Xpsm = NaN(nRows, nEns); | ||
for r = 1:nRows | ||
Xpsm(r,:) = m.X(F{s}.rows(r),:); | ||
end | ||
end | ||
end | ||
|
||
% Get the values for each prior and run the PSM | ||
for p = 1:nPriors | ||
Xrun = Xpsm(:,:,p); | ||
if nargout>1 | ||
[Yrun, Rrun] = F{s}.runPSM(Xrun); | ||
else | ||
Yrun = F{s}.runPSM(Xrun); | ||
end | ||
|
||
% Error check the R output | ||
if nargout>1 | ||
name = sprintf('R values for %s for prior %.f', F{s}.messageName(s), p); | ||
dash.assertVectorTypeN(Rrun, 'numeric', nEns, name); | ||
if ~isrow(Rrun) | ||
error('%s must be a row vector', name); | ||
end | ||
end | ||
|
||
% Error check the Y output | ||
name = sprintf('Y values for %s for prior %.f', F{s}.messageName(s), p); | ||
dash.assertVectorTypeN(Yrun, 'numeric', nEns, name); | ||
if ~isrow(Yrun) | ||
error('%s must be a row vector', name); | ||
end | ||
|
||
% Save | ||
Ye(s,:,p) = Yrun; | ||
if nargout>1 | ||
R(s,:,p) = Rrun; | ||
end | ||
end | ||
end | ||
end |
Oops, something went wrong.