Skip to content

Commit 7cd08f4

Browse files
committed
COPSE V2.0 release
Files copied from PALEOexeter repository branch: copseRLrelease tag: COPSE-V2.0 SHA1 ID: 77a62885441852dd1ddcde58f91329c8a6b726b2
0 parents  commit 7cd08f4

File tree

189 files changed

+16689
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

189 files changed

+16689
-0
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
output
2+
examples/copse/COPSE_test_output_full
3+
run_copse_tests.txt
4+
*~
5+
*asv

COPSE_setup.m

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
function COPSE_setup
2+
% Set up COPSE paths
3+
4+
% Add new directories to this list
5+
6+
paleodirs={'code','code/core','code/forcings', ...
7+
'code/copse', ...
8+
'code/configuration','code/utils', ...
9+
'libraries/YAMLMatlab_0.4.3', ...
10+
'examples/copse'};
11+
12+
paleopath = pwd;
13+
14+
% get current path as semicolon-separated list
15+
p = path;
16+
17+
% check for any PALEO or COPSE entries in current path
18+
19+
pentries = strsplit(p,pathsep);
20+
21+
pcopse = {};
22+
for i = 1:length(pentries)
23+
if ~isempty(strfind(pentries{i},'PALEO')) || ~isempty(strfind(pentries{i},'COPSE'))
24+
pcopse{end+1} = pentries{i};
25+
end
26+
end
27+
28+
% prompt user and remove any PALEO entries
29+
% (eg if there are two installations ...)
30+
if ~isempty(pcopse)
31+
fprintf('possible COPSE paths found\n');
32+
for i=1:length(pcopse)
33+
fprintf(' %s\n',pcopse{i})
34+
end
35+
36+
str = input('\nRemove these paths ? Y/N [Y]\n','s');
37+
if isempty(str)
38+
str = 'Y';
39+
end
40+
if strcmpi(str,'Y')
41+
for i=1:length(pcopse)
42+
fprintf('removing folder %s\n',pcopse{i});
43+
rmpath(pcopse{i});
44+
end
45+
end
46+
end
47+
48+
% Add PALEO paths
49+
50+
for i=1:length(paleodirs)
51+
psepdir = strrep(paleodirs{i},'/',filesep);
52+
fulldir = fullfile(paleopath,psepdir);
53+
fprintf('adding folder %s\n',fulldir);
54+
addpath(fulldir);
55+
end
56+
57+
% Test yaml hence preload - attempt to workaround an issue on linux with crash ?
58+
59+
yaml_file = 'libraries/YAMLMatlab_0.4.3/Tests/Data/test_import/file1.yaml';
60+
YamlStruct = ReadYaml(yaml_file);
61+
62+
63+

KnownIssues.txt

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
COPSE known issues 2017-12-01
2+
-----------------------------
3+
4+
Lenton etal (2017) COPSE Reloaded
5+
---------------------------------
6+
COPSE_reloaded_reloaded.m
7+
8+
1) Spurious warnings reported to screen when loading some comparison output datasets from older models.
9+
>> Warning: Cannot load an object of class 'copse_model_sfbw':
10+
(seen with some datasets in additional output data in COPSE_V2.0_test_output_full.zip)
11+
No effect on the data loaded.
12+
13+
Mills etal (2014) G3
14+
--------------------
15+
COPSE_mills2014g3_mills2014g3.m
16+
17+
The below minor fixes are made to this version of the model. These changes make little difference to output and do not alter the conclusions of the original paper. The combined effect of changes relative to the original paper can be demonstrated by editing COPSE_mills2014g3_mills2014g3.m to select
18+
run=copse_millsg3_millsg3_expts('g3mills2014nobugs', 'baseline');
19+
20+
1) Land biota T limitation effectively removed (copse_landbiota_mills2014g3.m)
21+
22+
To reproduce, set
23+
f_bug_g32014_landbiotatemp = 'Yes'
24+
See https://github.com/sjdaines/PALEOexeter/issues/23
25+
26+
Effect is: pCO2 is ~0.1 PAL low (200Ma-0), ~< 0.6 PAL low (250Ma-200Ma)
27+
pO2 (not plotted in paper) is ~< 0.06 PAL high (200Ma-0), ~<0.1 PAL high (250Ma-200Ma)
28+
29+
2) Sr concentration calculation was incorrect (not plotted in paper)
30+
(copse_model_mills2014g3.m)
31+
32+
f_bug_g32014_Srconc: 'Yes'
33+
34+
3) Granite area calculation only used CFB area (copse_landsurfaceareas_mills2014g3.m)
35+
36+
f_granitearea = 'G3original'
37+
38+
4) Degass forcing extrapolation to times earlier than 230Ma inconsistent
39+
(copse_force_vandermeer.m)
40+
DEGASS returned to background value (=1) at 230Ma, whereas OIB area assumed constant area for T > 230Ma
41+
42+
copse_force_vandermeer.extrapolate = 3
43+
44+
5) Small (~1%) discrepancy between basalt area calculated from LIP table vs offline calculation.
45+
(test_copse_load_phanlip('mills2014g3'), ie copse_load_phanlip.m vs copse_force_revision_ba.m + either
46+
bas_area_mills2014.xlsx or ggge20620-sup-0002-suppinfo2.mat)
47+
48+
https://github.com/sjdaines/PALEOexeter/issues/18
49+
50+
6) Time-evolution of delta_Sr_sed
51+
(copse_model_mills2014g3.m)
52+
53+
Updated in copse_model_reloaded.m
54+
55+
7) Silicate weathering used 288 not 288.15 K offset, resulting in ~1% offset in pCO2
56+
(copse_weathering_rates_mills2014g3.m)
57+
pars.f_act_energies = 'split_bug_g32014_Toffset'
58+
59+
https://github.com/sjdaines/PALEOexeter/issues/21
60+
61+
Bergman etal (2004)
62+
-------------------
63+
COPSE_bergman2004_bergman2004.m
64+
65+
1) Organic carbon degassing rolloff at low pO2 (copse_model_bergman2004.m)
66+
# COPSE 5_14 C code (and Bergman 2004) use 'O2copsecrashprevent' which rolls
67+
# off organic carbon degassing at low pO2. This has a big effect at low pO2 when
68+
# oxidative weathering is oxygen-independent (ie Ordovician and earlier)
69+
70+
f_ocdeg : 'O2indep' # options 'O2indep', 'O2copsecrashprevent'
71+
72+
2) Marine N (unlikely to affect results as N adjusts due to denitrification alone)
73+
(copse_marinebiota_bergman2004.m)
74+
75+
f_nfix_nreplete: 'Off' # options 'Off','Sign' (COPSE 5_14 C code has 'Sign')
76+

README.txt

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
2+
The COPSE (Carbon, Oxygen, Phosphorus, Sulphur and Evolution) biogeochemical model predicts the coupled histories and controls on atmospheric O2, CO2 and ocean composition over Phanerozoic time.
3+
4+
The model is described in the following publications:
5+
6+
Bergman, N. M., Lenton, T. M., & Watson, A. J. (2004). COPSE: A new model of biogeochemical cycling over Phanerozoic time. American Journal of Science, 304(5), 397�437. http://doi.org/10.2475/ajs.304.5.397
7+
8+
Mills, B., Daines, S. J., & Lenton, T. M. (2014). Changing tectonic controls on the long-term carbon cycle from Mesozoic to present. Geochemistry, Geophysics, Geosystems, 15(12), 4866�4884. http://doi.org/10.1002/2014GC005530
9+
10+
Lenton, T. M., Dahl, T. W., Daines, S. J., Mills, B. J. W., Ozaki, K., Saltzman, M. R., & Porada, P. (2016). Earliest land plants created modern levels of atmospheric oxygen. Proceedings of the National Academy of Sciences, 113(35), 9704�9709. http://doi.org/10.1073/pnas.1604787113
11+
12+
Lenton, T. M., Daines, S.J., Mills, B. J. W. (2017). COPSE reloaded: An improved model of biogeochemical cycling over Phanerozoic time. Earth Science Reviews, in revision.
13+
14+
15+
Running the model (requires Matlab version 2012 or higher):
16+
-----------------
17+
18+
>> COPSE_setup % sets Matlab paths
19+
>> cd examples/copse
20+
>> run_copse_tests % test against archived output (output is included for only the default set of 7 tests)
21+
>>
22+
>> COPSE_reloaded_reloaded % runs model, plots output for Lenton etal (2017)
23+
>> COPSE_bergman2004_bergman2004 % Bergman etal (2004) version
24+
>> COPSE_millsg3_millsg3 % Mills etal (2014) version
25+
>> COPSE_reloaded_bergman2004 % includes results from Lenton etal (2016) - see comments in file
26+
27+
28+
Evaluation data:
29+
---------------
30+
31+
Datasets are not part of the public release but are available on request from the authors.
32+
33+
Known issues:
34+
------------
35+
36+
Please see KnownIssues.txt
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
function strct = copse_modify_struct( strct, fld, newval )
2+
%Modify a struct, error if field doesn't already exist
3+
4+
if ~isfield(strct,fld)
5+
error ('struct "%s" no field "%s"',inputname(1),fld);
6+
end
7+
8+
strct.(fld)=newval;
9+
10+
end
11+
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
classdef paleo_modelbuilder
2+
% Create and configure a model from configuration file.
3+
%
4+
%
5+
methods(Static)
6+
function tm = createModel(configfile, configname)
7+
8+
LN = 'paleo_modelbuilder.createModel'; L = paleo_log.getLogger();
9+
10+
% Read configuration from yaml file
11+
cfgset = paleo_parameterset(configfile, configname);
12+
13+
% Create model:
14+
% locate the relevant section in the cfg structure
15+
modelcfg = paleo_parameterset.findElem(cfgset.configdata, 'model');
16+
% create the model
17+
L.log(L.DEBUG, LN, sprintf('creating tm ctorstr=''%s''\n', modelcfg.class));
18+
tm = eval(modelcfg.class);
19+
20+
%%%%%%%% Set forcing functions for this run
21+
tm.force = {};
22+
forcecfg = paleo_parameterset.findElem(cfgset.configdata, 'model.force');
23+
for i = 1:length(forcecfg)
24+
ctorstr = forcecfg{i};
25+
newforce = eval(ctorstr);
26+
tm.force{end+1} = newforce;
27+
end
28+
29+
tm.perturb = {};
30+
perturbcfg = paleo_parameterset.findElem(cfgset.configdata, 'model.perturb');
31+
for i = 1:length(perturbcfg)
32+
ctorstr = perturbcfg{i};
33+
newperturb = eval(ctorstr);
34+
tm.perturb{end+1} = newperturb;
35+
end
36+
37+
%%%%%%%%%% Set top-level parameters
38+
tm.pars = cfgset.configureobject(tm.pars, 'model.pars', 'pars');
39+
40+
end
41+
42+
end
43+
end
44+
45+
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
classdef paleo_parameterset
2+
% A parameter set contains 'configdata' read from 'configname' in yaml 'configfile'.
3+
%
4+
% The yaml configfile should contain configuration data at the top level, eg
5+
%
6+
% copse_reloaded:
7+
% <configuration>
8+
%
9+
% copse_bergman2004:
10+
% <another configuration>
11+
%
12+
13+
properties(SetAccess=private)
14+
configfile = ''; % configfile read
15+
configname = ''; % named configuration from that file
16+
17+
configdata; % struct with data from configfile.(configname)
18+
end
19+
20+
properties(Constant)
21+
reservedFields = {'class'}; % fields in config file to ignore;
22+
end
23+
24+
methods
25+
function obj = paleo_parameterset(configfile, configname)
26+
% Read configuration data from yaml parameter file (which may have multiple configs within)
27+
28+
% Read entire parameter file (which may contain multiple configurations)
29+
obj.configfile = configfile;
30+
filects = ReadYaml(configfile);
31+
32+
% Get the data for the specified configname
33+
obj.configname = configname;
34+
if isfield(filects, obj.configname)
35+
obj.configdata = filects.(obj.configname);
36+
else
37+
error('no configname %s in configfile %s', obj.configname, obj.configfile);
38+
end
39+
end
40+
41+
42+
function target = configureobject(obj, target, configpath, targetname)
43+
% Set properties/fields on a single object 'target' from location 'configpath' in 'configdata'
44+
%
45+
% Input:
46+
% target - object to be configured
47+
% configpath - path within obj.configdata containing configuration for 'target'
48+
% targetname - [optional] name of target object. This makes the target object fields
49+
% available when evaluating a parameter, to allow eg par1 = (2*pars.par2)
50+
%
51+
% Returns:
52+
% target - configured object
53+
54+
LN = 'paleo_parameterset.configureobject'; L = paleo_log.getLogger(LN);
55+
56+
57+
% Find the configuration data at location configpath
58+
% (configdata has been read from file, configpath is specified as an argument)
59+
targetconfig = paleo_parameterset.findElem(obj.configdata, configpath);
60+
61+
if isempty(targetconfig)
62+
error('no config for configfile %s configname %s configpath %s', obj.configfile, obj.configname, configarg);
63+
else
64+
% configure target ...
65+
66+
rpars = fields(targetconfig);
67+
68+
% apply config to target object properties, ignoring reserved fields
69+
for rp = 1:length(rpars)
70+
if ~any(strcmp(rp, obj.reservedFields)) % ignore reserved fields
71+
subval = paleo_parameterset.subValue(targetconfig.(rpars{rp}), targetname, target);
72+
L.log(L.TRACE, LN, sprintf('<target>.%s=%s\n', rpars{rp}, subval));
73+
target.(rpars{rp}) = subval;
74+
end
75+
end
76+
end
77+
end
78+
end
79+
80+
81+
methods(Static)
82+
function subval = subValue(val, ctxtname, ctxtdata)
83+
% parameter substitution.
84+
%
85+
% Input:
86+
% val - parameter value either as a string (which will be evaluated if an expression in round brackets),
87+
% or a string without brackets or non-string (which will just be returned directly)
88+
% ctxtname - [optional] name of a struct or object (eg 'pars') to provide as context for evaluation.
89+
% ctxtdata - [optional] value of this struct or object
90+
%
91+
% Returns:
92+
% subval - parameter value, either as-supplied if non-string, or evaluated from a supplied string
93+
94+
if nargin < 2
95+
ctxtname = '';
96+
end
97+
98+
if ischar(val)
99+
% evaluate string-valued parameter if of from '(expr)'
100+
101+
% guard against inadvertent use of 'false' or 'true' which are parsed as strings, not logicals
102+
% use true and false (no quotes)
103+
if any(strcmp(val,{'false','true'}))
104+
error('''false'' and ''true'' strings illegal in parameter value - remove quotes');
105+
end
106+
% TODO guard against strings that look like they might be supposed to be numeric expressions
107+
% or attempts to set numeric fields to strings?
108+
109+
% If string is of form '(expr)', evaluate
110+
if ~isempty(val) && strcmp(val(1),'(') && strcmp(val(end),')')
111+
% evaluate contents of brackets so (2+2) = 4 etc
112+
if ~isempty(ctxtname)
113+
% provide optional context for evaluation
114+
eval([ctxtname '= ctxtdata;']);
115+
end
116+
subval = eval(val);
117+
else
118+
% no brackets - just return the raw string
119+
subval = val;
120+
end
121+
else
122+
% return unmodified non-string value
123+
subval = val;
124+
end
125+
end
126+
127+
128+
function elem = findElem(data, path)
129+
% find element within structure-tree 'data' at location 'path'
130+
% eg
131+
% data.x.y = elem
132+
% path = 'x.y'
133+
134+
% split path on .
135+
psplit = strsplit(path,'.');
136+
137+
% navigate into data to find elem
138+
elem = data;
139+
if ~isempty(psplit{1}) % NB: strsplit('','.') returns {''}
140+
for i=1:length(psplit)
141+
if isfield(elem, psplit{i})
142+
elem = elem.(psplit{i});
143+
else
144+
error('path %s not present', path);
145+
end
146+
end
147+
end
148+
end
149+
end
150+
end
151+

0 commit comments

Comments
 (0)