For development purposes, new variables and datatypes need to be introduced into ACE. Fortunately, as ACE is written in python, it can exploit object oriented code principles so that new attributes can be linked to a sample without any special formatting. For example, in the code sample attributes are defined as objects within the sample:
- s[“id’] is the sample name,
- s[“latitude”] is the sample latitude,
- s[“erosion rate”] is the erosion rate for the sample.
For variables which are imported to and exported from ACE these attributes and units are listed here (in) and here (out). However, in ACE intermediate and computed variables are also attached to each sample in the code. For example:
- s[“rigidity”] for the cutoff rigidity of a sample at a particular timestep
- s[“age”] for the computed sample age
When developing ACE, any variable name can be used and attached to a sample for development purposes. However to be shown at the end of an ACE calculation as a new column in the Sample Browser, the ‘Output Att’ checkbox must be ticked in the Attribute Editor. This is demonstrated below.
Here we show how to introduce new variables into ACE. The variable will be the difference between calculated and independent ages for a calibration sample. This variable will then show how accurately any experiment reproduces the independently determined age of a calibration sample. This example is a little silly, as an easy solution to compare ages would be to select the ‘Ages’ option in the View menu of the Sample Browser, and then export it to a csv file:
Then the difference between ages could be calculated in a spreadsheet program. However, this would only work for existing sample attributes, not intermediate or user created ones.
First, create the new variable in the Attribute Editor. Let’s call it ‘age difference’. As it will be a number, not a string, we allocate it the type ‘Float’:
And we select Output Att to make this attribute available for viewing at the end of the calculation. Now repeat this process for the uncertainty in the age difference, which can be called ‘age difference uncertainty’.
Now to the code. As sample ages are almost the final things computed by the dating workflows, the calculation of age difference should be near the end of the procedure. Here is the workflow for the 36Cl Time Stepping Dating workflow, listed in the ACE user repository ‘workflows’ directory:
dating ["36Cl"] InitInventories => ChemicalCompositions ChemicalCompositions => StepForInventories StepForInventories.loop => Factor<geomagneticIntensity> Factor<geomagneticIntensity> => Factor<seaLevel> Factor<seaLevel> => InstElevation InstElevation => AtmosphericPressure AtmosphericPressure => Factor<geomagneticLatitude> Factor<geomagneticLatitude> => Factor<geographicScaling> Factor<geographicScaling> => StepDiffusionEquation StepDiffusionEquation => SCPDating SCPDating => NonCosmogenicProduction NonCosmogenicProduction => InventoryChangeCalculation InventoryChangeCalculation => StepForInventories StepForInventories => OutputFor36ClDating
All of the modules are in the ACE Components directory (src/ACE/Components/) and the last procedure is called OutputFor36ClDating. This file can be opened with any text editor:
def calculateInvcUncertainty(self, s): # calculate =SQRT(measured inventory uncertainty^2+(0.2*Inv_r)^2) prod1 = 0.2 * s["nucleogenic inventory"] term1 = s["measured inventory uncertainty"]**2.0 term2 = prod1**2.0 term3 = s["Inv_c_uncertainty"]**2.0 # Do not include production rate uncertainty #sum1 = term1 + term2 + term3 sum1 = term1 + term2 s["cosmogenic inventory uncertainty"] = math.sqrt(sum1)
This routine computes the cosmogenic inventory uncertainty, which from the above is calculated as the square root of the sum of squared measured and nucleogenic inventory uncertainties. And actually, it doesn’t even do that, as this routine is not called within OutputFor36ClDating.py. To use this routine, we need to add the following below the ‘super’ line:
super(OutputFor36ClDating, self).__call__(samples)
for s in samples: self.calculateAgeDifference(s);
This routine a good place to calculate the difference between computed and independent ages for calibration samples. In the code we add the following lines, defined as (to match the call statement above) calculateAgeDifference:
def calculateAgeDifference(self, s): # calculate the difference in computed and independent ages of samples if s["independent age"] > 0: s["age difference"] = s["age"]-s["independent age"] #Now calculate uncertainty in age difference term1 = s["age uncertainty"]**2.0 term2 = s["indepdent age uncertainty"]**2.0 sum1 = term1 + term2 s["age difference uncertainty"] = math.sqrt(sum1)
The if statement checks that the sample has an independent age, so that the code only operates for samples from the calibration databases. Note the Python conventions here, a colon at the end of an if statement, and a 2 space indentation to show the body of code to execute when the if statement is true.
Now date some calibration samples using the workflow. Then to make viewing results easier (and to check the numbers), add the new attributes to the Ages option of the View menu using the View Editor:
Now differences can be viewed alongside existing ages:
The age differences can then be plotted to see how well computed sample ages match independent ages. For example, the following plot was made by sorting first by age difference and then by id in the Sample Browser, and clicking ‘plot’:
So ignoring the uncertainties, the default 36Cl experiment underestimates the age of sample 9353 the most (top), by under 2000 years, and overestimates the age of sample MC-5 the most (bottom), by over 4000 years. Could this be a result of secular variability? If so there should be a trend with sample age, which we could see by plotting age difference against indepedent age:
The appended file OutputFor36ClDating.py can be downloaded here. If you use it, don’t forget to create the new variables ‘age difference’ and ‘age difference uncertainty’ in the Attribute Editor.