Skip to content

Commit a680386

Browse files
committed
Fix unit conversion
1 parent 2e88e8b commit a680386

1 file changed

Lines changed: 97 additions & 101 deletions

File tree

mpas_analysis/ocean/conservation.py

Lines changed: 97 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -178,29 +178,29 @@ def setup_and_check(self):
178178
self.plotTypes = self.config.getexpression('timeSeriesConservation', 'plotTypes')
179179

180180
self.masterVariableList = {'absolute_energy_error': ['absoluteEnergyError'],
181-
'total_energy_flux': ['netEnergyFlux'],
182-
'absolute_salt_error': ['absoluteSaltError'],
183-
'ice_salt_flux': ['netSaltFlux'],
184-
'total_mass_flux': ['netMassFlux'],
185-
'total_mass_change': ['netMassChange'],
186-
'land_ice_mass_change': ['landIceMassChange'],
187-
'land_ice_ssh_change': ['landIceSshChange'],
188-
'land_ice_mass_flux': ['landIceMassFlux'],
189-
'land_ice_mass_flux_components': ['accumulatedIcebergFlux',
181+
'total_energy_flux': ['netEnergyFlux'],
182+
'absolute_salt_error': ['absoluteSaltError'],
183+
'ice_salt_flux': ['netSaltFlux'],
184+
'total_mass_flux': ['netMassFlux'],
185+
'total_mass_change': ['netMassChange'],
186+
'land_ice_mass_change': ['landIceMassChange'],
187+
'land_ice_ssh_change': ['landIceSshChange'],
188+
'land_ice_mass_flux': ['landIceMassFlux'],
189+
'land_ice_mass_flux_components': ['accumulatedIcebergFlux',
190190
'accumulatedLandIceFlux',
191191
'accumulatedRemovedRiverRunoffFlux',
192192
'accumulatedRemovedIceRunoffFlux']}
193193

194194
# for each derived variable, which source variables are needed
195-
self.derivedVariableList = {'netMassChange': ['netMassFlux'],
195+
self.derivedVariableList = {'netMassChange': ['massChange'],
196196
'landIceMassFlux': ['accumulatedIcebergFlux',
197-
'accumulatedLandIceFlux',
198-
'accumulatedRemovedRiverRunoffFlux',
199-
'accumulatedRemovedIceRunoffFlux'],
197+
'accumulatedLandIceFlux',
198+
'accumulatedRemovedRiverRunoffFlux',
199+
'accumulatedRemovedIceRunoffFlux'],
200200
'landIceSshChange': ['accumulatedIcebergFlux',
201-
'accumulatedLandIceFlux',
202-
'accumulatedRemovedRiverRunoffFlux',
203-
'accumulatedRemovedIceRunoffFlux'],
201+
'accumulatedLandIceFlux',
202+
'accumulatedRemovedRiverRunoffFlux',
203+
'accumulatedRemovedIceRunoffFlux'],
204204
'landIceMassChange': ['accumulatedIcebergFlux',
205205
'accumulatedLandIceFlux',
206206
'accumulatedRemovedRiverRunoffFlux',
@@ -303,6 +303,7 @@ def _make_plot(self, plot_type):
303303
titles['absolute_salt_error'] = 'Salt conservation error'
304304
titles['total_mass_flux'] = 'Total mass flux'
305305
titles['total_mass_change'] = 'Total mass anomaly'
306+
titles['land_ice_mass_flux'] = 'Mass flux due to land ice'
306307
titles['land_ice_mass_change'] = 'Mass anomaly due to land ice fluxes'
307308
titles['land_ice_ssh_change'] = 'SSH anomaly due to land ice fluxes'
308309
titles['land_ice_mass_flux_components'] = 'Mass fluxes from land ice'
@@ -314,6 +315,7 @@ def _make_plot(self, plot_type):
314315
y_labels['absolute_salt_error'] = 'Salt (Gt)'
315316
y_labels['total_mass_flux'] = 'Mass flux (Gt/yr)'
316317
y_labels['total_mass_change'] = 'Mass (Gt)'
318+
y_labels['land_ice_mass_flux'] = 'Mass flux (Gt/yr)'
317319
y_labels['land_ice_mass_change'] = 'Mass (Gt)'
318320
y_labels['land_ice_ssh_change'] = 'SSH anomaly (mm)'
319321
y_labels['land_ice_mass_flux_components'] = 'Mass flux (Gt/yr)'
@@ -326,6 +328,7 @@ def _make_plot(self, plot_type):
326328
captions['absolute_salt_error'] = 'Absolute salt conservation error'
327329
captions['total_mass_flux'] = 'Total mass flux'
328330
captions['total_mass_change'] = 'Total mass anomaly'
331+
captions['land_ice_mass_flux'] = 'Mass flux due to land ice'
329332
captions['land_ice_mass_change'] = 'Mass anomaly due to land ice fluxes'
330333
captions['land_ice_ssh_change'] = 'SSH anomaly due to land ice fluxes. Assumes a constant ocean area.'
331334
captions['land_ice_mass_flux_components'] = 'Mass flux components from land ice'
@@ -374,16 +377,7 @@ def _make_plot(self, plot_type):
374377
lineColors = []
375378
lineStyles = []
376379
for index, varname in enumerate(self.masterVariableList[plot_type]):
377-
if varname in self.derivedVariableList:
378-
variable = self._derive_variable(ds, varname)
379-
else:
380-
variable = ds[varname]
381-
if 'Removed' in varname:
382-
variable = -variable
383-
if 'mass_flux' or 'salt_flux' in plot_type:
384-
variable = variable * 1e-12 * constants.sec_per_year # convert to Gt/yr
385-
if 'salt_error' in plot_type:
386-
variable = variable * 1e-12 # convert to Gt
380+
variable = self._get_variable(ds, varname)
387381
fields.append(variable)
388382
legend_text = ''
389383
if self.referenceRunName != 'None':
@@ -396,16 +390,7 @@ def _make_plot(self, plot_type):
396390
lineColors.append(config.get('timeSeries', 'mainColor'))
397391
lineStyles.append(lineStylesBase[index])
398392
if self.referenceRunName != 'None':
399-
if varname in self.derivedVariableList:
400-
variable = self._derive_variable(ds_ref_slice, varname)
401-
else:
402-
variable = ds_ref_slice[varname]
403-
if 'Removed' in varname:
404-
variable = -variable
405-
if 'mass_flux' or 'salt_flux' in plot_type:
406-
variable = variable * 1e-12 * constants.sec_per_year # convert to Gt/yr
407-
if 'salt_error' in plot_type:
408-
variable = variable * 1e-12 # convert to Gt
393+
variable = self._get_variable(ds, varname)
409394
fields.append(variable)
410395
legend_text = self.referenceRunName
411396
if len(self.masterVariableList[plot_type]) > 1:
@@ -428,7 +413,7 @@ def _make_plot(self, plot_type):
428413
firstYearXTicks = None
429414

430415
if config.has_option('timeSeries', 'yearStrideXTicks'):
431-
EearStrideXTicks = config.getint('timeSeries',
416+
yearStrideXTicks = config.getint('timeSeries',
432417
'yearStrideXTicks')
433418
else:
434419
yearStrideXTicks = None
@@ -446,15 +431,6 @@ def _make_plot(self, plot_type):
446431
# save the plot to the output file
447432
plt.savefig(outFileName)
448433

449-
# here's an example of how you would create an XML file for this plot
450-
# with the appropriate entries. Some notes:
451-
# * Gallery groups typically represent all the analysis from a task,
452-
# or sometimes from multiple tasks
453-
# * A gallery might be for just for one set of observations, one
454-
# season, etc., depending on what makes sense
455-
# * Within each gallery, there is one plot for each value in
456-
# 'plotParameters', with a corresponding caption and short thumbnail
457-
# description
458434
caption = captions[plot_type]
459435
write_image_xml(
460436
config=self.config,
@@ -468,59 +444,83 @@ def _make_plot(self, plot_type):
468444
imageDescription=caption,
469445
imageCaption=caption)
470446

471-
def _derive_variable(self, ds, varname):
472-
473-
if varname == 'netMassChange':
474-
# Convert from kg/s to Gt/s
475-
mass_flux = ds['netMassFlux'] * 1e-12
476-
# Assume that the frequency of output is monthly
477-
dt = constants.sec_per_month
478-
# Convert from Gt/yr to Gt
479-
derived_variable = mass_flux.cumsum(axis=0) * dt
480-
elif varname == 'landIceMassChange':
481-
land_ice_mass_flux = self._derive_variable(ds, 'landIceMassFlux')
482-
# Convert from kg/s to Gt/s
483-
land_ice_mass_flux = land_ice_mass_flux * 1e-12
484-
# Assume that the frequency of output is monthly
485-
dt = constants.sec_per_month
486-
# Convert from Gt/yr to Gt
487-
derived_variable = land_ice_mass_flux.cumsum(axis=0) * dt
488-
489-
elif varname == 'landIceMassFlux':
490-
# Here, keep units as kg/s because the conversion to Gt/yr will happen later
491-
derived_variable = ds['accumulatedIcebergFlux'] + \
492-
ds['accumulatedLandIceFlux'] + \
493-
-ds['accumulatedRemovedRiverRunoffFlux'] + \
494-
-ds['accumulatedRemovedIceRunoffFlux']
495-
496-
elif varname == 'landIceSshChange':
497-
ts_files = sorted(self.historyStreams.readpath(
498-
'timeSeriesStatsMonthlyOutput',
499-
startDate=f'{self.startYear:04d}-01-01_00:00:00',
500-
endDate=f'{self.endYear:04d}-01-01_00:00:00',
501-
calendar=self.calendar))
502-
# Note that here we assume that the area of the ocean is constant in time
503-
# to save computational expense because most configurations do not allow
504-
# the area of the ocean to change
505-
ts_file = ts_files[0]
506-
if not os.path.exists(ts_file):
507-
raise ValueError(f'Could not find timeMonthlyStats file {ts_file}')
508-
var = 'timeMonthly_avg_areaCellGlobal'
509-
ds_ts = open_mpas_dataset(fileName=ts_file,
510-
calendar=self.calendar,
511-
variableList=[var])
512-
A = ds_ts[var].mean()
513-
land_ice_mass_change = self._derive_variable(ds, 'landIceMassChange')
514-
# Convert from to Gt to kg
515-
land_ice_mass_change = land_ice_mass_change * 1e12
516-
rho = self.namelist.getfloat('config_density0')
517-
# Convert from to kg to mm
518-
derived_variable = land_ice_mass_change * 1.0e3 / (rho * A)
519-
447+
def _get_variable(self, ds, varname, mks=False):
448+
if varname not in self.derivedVariableList:
449+
variable = ds[varname]
520450
else:
521-
raise ValueError(f'Attempted to derive non-supported variable {varname}')
451+
# Here we keep the units mks
452+
if varname == 'netMassChange':
453+
variable = self._get_variable(ds, 'massChange', mks=True)
454+
# mass_flux = self._get_variable(ds, 'netMassFlux')
455+
# # Assume that the frequency of output is monthly
456+
# dt = constants.sec_per_month
457+
# # Convert from kg/s to kg
458+
# derived_variable = mass_flux.cumsum(axis=0) * dt
459+
elif varname == 'landIceMassChange':
460+
land_ice_mass_flux = self._get_variable(ds, 'landIceMassFlux', mks=True)
461+
# Assume that the frequency of output is monthly
462+
dt = constants.sec_per_month
463+
# Convert from kg/s to kg/month
464+
land_ice_mass_flux = land_ice_mass_flux * dt
465+
# Convert from kg/month to kg
466+
variable = np.cumsum(land_ice_mass_flux)
467+
468+
elif varname == 'landIceMassFlux':
469+
variable = self._get_variable(ds, 'accumulatedIcebergFlux', mks=True) + \
470+
self._get_variable(ds, 'accumulatedLandIceFlux', mks=True) + \
471+
self._get_variable(ds, 'accumulatedRemovedRiverRunoffFlux', mks=True) + \
472+
self._get_variable(ds, 'accumulatedRemovedIceRunoffFlux', mks=True)
473+
474+
elif varname == 'landIceSshChange':
475+
ts_files = sorted(self.historyStreams.readpath(
476+
'timeSeriesStatsMonthlyOutput',
477+
startDate=f'{self.startYear:04d}-01-01_00:00:00',
478+
endDate=f'{self.endYear:04d}-01-01_00:00:00',
479+
calendar=self.calendar))
480+
# Note that here we assume that the area of the ocean is constant in time
481+
# to save computational expense because most configurations do not allow
482+
# the area of the ocean to change
483+
ts_file = ts_files[0]
484+
if not os.path.exists(ts_file):
485+
raise ValueError(f'Could not find timeMonthlyStats file {ts_file}')
486+
var = 'timeMonthly_avg_areaCellGlobal'
487+
ds_ts = open_mpas_dataset(fileName=ts_file,
488+
calendar=self.calendar,
489+
variableList=[var])
490+
A = ds_ts[var].mean()
491+
land_ice_mass_change = self._get_variable(ds, 'landIceMassChange', mks=True)
492+
rho = self.namelist.getfloat('config_density0')
493+
# Convert from to kg to m
494+
variable = land_ice_mass_change / (rho * A)
522495

523-
return derived_variable
496+
else:
497+
raise ValueError(f'Attempted to derive non-supported variable {varname}')
498+
499+
removed_vars = ['accumulatedRemovedRiverRunoffFlux',
500+
'accumulatedRemovedIceRunoffFlux']
501+
if varname in removed_vars:
502+
variable = -variable
503+
504+
if not mks:
505+
# Here we do all the unit conversion from mks into whatever we want
506+
mass_vars = ['initialMass', 'finalMass', 'absoluteMassError',
507+
'relativeMassError', 'massChange', 'landIceMassChange']
508+
salt_vars = ['initialSalt', 'finalSalt', 'absoluteSaltError',
509+
'relativeSaltError']
510+
mass_flux_vars = ['netMassFlux', 'landIceMassFlux']
511+
salt_flux_vars = ['netSaltFlux']
512+
ssh_vars = ['landIceSshChange', 'sshChange']
513+
if (varname in mass_vars) or (varname in salt_vars):
514+
# Convert from kg to Gt
515+
variable = variable * 1e-12
516+
if (varname in mass_flux_vars) or (varname in salt_flux_vars):
517+
# Convert from kg/s to Gt/yr
518+
variable = variable * 1e-12 * constants.sec_per_year
519+
if varname in ssh_vars:
520+
# Convert from m to mm
521+
variable = variable * 1e3
522+
523+
return variable
524524

525525
def _compute_time_series_with_ncrcat(self, variable_list):
526526

@@ -531,10 +531,6 @@ def _compute_time_series_with_ncrcat(self, variable_list):
531531
------
532532
OSError
533533
If ``ncrcat`` is not in the system path.
534-
535-
Author
536-
------
537-
Xylar Asay-Davis
538534
"""
539535

540536
if find_executable('ncrcat') is None:

0 commit comments

Comments
 (0)