Examples: Modifying data in individual fields¶
Example 1: Set all real data fields to zero¶
import mule
from mule.operators import ScaleFactorOperator
zero_operator = ScaleFactorOperator(0.0)
ff = mule.FieldsFile.from_file("InputFile")
ff_out = ff.copy()
for field in ff.fields:
if field.lbrel in (2,3) and field.lbuser4 != 30:
ff_out.fields.append(zero_operator(field))
else:
ff_out.fields.append(field)
ff_out.to_file("OutputFile")
Note
A copy of the original file object is not strictly necessary, but it avoids overwriting the original field objects.
The built-in operators in mule.operators handle mdi points in a sensible way - in this example points set to mdi will not be changed.
Notice the check on lbrel - this is to make sure only fields with valid header release numbers are processed.
The loop also filters out fields with the STASH code 30 - this is the land-sea mask, and probably should not be zeroed; note that since only fields with valid header releases define the lbuser4 property it is important that the two if conditions are placed in the order above.
Since the packing code (lbpack) of the modified fields is not changed Mule will try to write the fields out with the same packing method and accuracy - in some cases this might not be suitable so you may need to consider whether to update the value of lbpack on each field before appending it to the file.
It is also common practice to update the lbproc value of the field’s lookup header when the data has been modified (some applications use this to determine certain types of field) - you should update this value before appending the modified fields to the file if required (or do it inside the operator’s new_field method).
Example 2: Set level-1 u fields (STASH code 2) to their absolute values¶
import mule
import numpy as np
class AbsoluteOperator(mule.DataOperator):
"""Operator which sets all non-missing points to their absolute value"""
def __init__(self):
pass
def new_field(self, source_field):
"""Creates the new field object"""
field = source_field.copy()
return field
def transform(self, source_field, new_field):
"""Performs the data manipulation"""
data = source_field.get_data()
data_out = np.abs(data)
# If the field defines MDI, reset missing points from original
# field back to MDI in the output
if hasattr(source_field, "bmdi"):
mdi = source_field.bmdi
mask = (data == mdi)
data_out[mask] = mdi
return data_out
abs_operator = AbsoluteOperator()
ff = mule.FieldsFile.from_file("InputFile")
for ifield, field in enumerate(ff.fields):
if field.lbrel in (2,3) and field.lbuser4 == 2 and field.lblev == 1:
ff.fields[ifield] = abs_operator(field)
ff.to_file("OutputFile")
Note
There aren’t any built-in operators in mule.operators which provide this functionality, so this example creates a custom operator.
Unlike example 1 - this example modifies the fields in the file object in-place; this is neater but overwrites the point of access to the original fields so might not always be ideal.
Notice the check on lbrel - this is to make sure only fields with valid header release numbers are processed.
The loop also filters for the required STASH code and level; note that since only fields with valid header release numbers define lbuser4 and lblev, so it is important that the if conditions for these appear after the check on lbrel.
Since the packing code (lbpack) of the modified fields is not changed Mule will try to write the fields out with the same packing method and accuracy - in some cases this might not be suitable so you may need to consider whether to update the value of lbpack on each field before appending it to the file (or inside the operator’s new_field method).
It is also common practice to update the lbproc value of the field’s lookup header when the data has been modified (some applications use this to determine certain types of field) - you should update this value before appending the modified fields to the file if required (or do it inside the operator’s new_field method).
Example 3: Raise all fields valid at 12Z to the power 3¶
import mule
class ExponentOperator(mule.DataOperator):
"""Operator which raises all non-missing points to an exponent"""
def __init__(self, exponent):
"""Initialise the operator, setting the exponent value"""
self.exponent = exponent
def new_field(self, source_field):
"""Creates the new field object"""
field = source_field.copy()
return field
def transform(self, source_field, new_field):
"""Performs the data manipulation"""
data = source_field.get_data()
data_out = data**self.exponent
# If the field defines MDI, reset missing points from original
# field back to MDI in the output
if hasattr(source_field, "bmdi"):
mdi = source_field.bmdi
mask = (data == mdi)
data_out[mask] = mdi
return data_out
exp_operator = ExponentOperator(3)
ff = mule.FieldsFile.from_file("InputFile")
for ifield, field in enumerate(ff.fields):
if field.lbrel in (2,3) and field.lbhr == 12:
ff.fields[ifield] = exp_operator(field)
ff.to_file("OutputFile")
Note
There aren’t any built-in operators in mule.operators which provide this functionality, so this example creates a custom operator.
Unlike example 1 - this example modifies the fields in the file object in-place; this is neater but overwrites the point of access to the original fields so might not always be ideal.
Notice the check on lbrel - this is to make sure only fields with valid header release numbers are processed.
The loop also filters for the required forecast time; note that since only fields with valid header release numbers define lbhr it is important that the if conditions for these appear after the check on lbrel.
Since the packing code (lbpack) of the modified fields is not changed Mule will try to write the fields out with the same packing method and accuracy - in some cases this might not be suitable so you may need to consider whether to update the value of lbpack on each field before appending it to the file (or inside the operator’s new_field method).
It is also common practice to update the lbproc value of the field’s lookup header when the data has been modified (some applications use this to determine certain types of field) - you should update this value before appending the modified fields to the file if required (or do it inside the operator’s new_field method).
Example 4: Remove negative specific humidities (STASH code 10)¶
import mule
from mule.operators import HardLimitOperator
neg_to_zero_operator = HardLimitOperator(lower_limit=0.0)
ff = mule.FieldsFile.from_file("InputFile")
for ifield, field in enumerate(ff.fields):
if field.lbrel in (2,3) and field.lbuser4 == 10:
ff.fields[ifield] = neg_to_zero_operator(field)
ff.to_file("OutputFile")
Note
Unlike example 1 - this example modifies the fields in the file object in-place; this is neater but overwrites the point of access to the original fields so might not always be ideal.
Notice the check on lbrel - this is to make sure only fields with valid header release numbers are processed.
The loop also filters for the required STASH code; note that since only fields with valid header release numbers define lbuser4 it is important that the if conditions for these appear after the check on lbrel.
Example 5: Set all level-38 fields to -1¶
import mule
from mule.operators import ScaleFactorOperator, AddScalarOperator
zero_operator = ScaleFactorOperator(0.0)
subtract_one_operator = AddScalarOperator(-1.0)
ff = mule.FieldsFile.from_file("InputFile")
for ifield, field in enumerate(ff.fields):
if field.lbrel in (2,3) and field.lblev == 38:
ff.fields[ifield] = (
subtract_one_operator(zero_operator(field)))
ff.to_file("OutputFile")
Note
There isn’t a single built-in operator in mule.operators which provides this functionality, but it can be achieved by combining the effects of 2 operators - note the way in which they can be “chained” together to apply multiple operations.
Unlike example 1 - this example modifies the fields in the file object in-place; this is neater but overwrites the point of access to the original fields so might not always be ideal.
Notice the check on lbrel - this is to make sure only fields with valid header release numbers are processed.
The loop also filters for the required level; note that since only fields with valid header release numbers define lblev it is important that the if conditions for these appear after the check on lbrel.
Since the packing code (lbpack) of the modified fields is not changed Mule will try to write the fields out with the same packing method and accuracy - in some cases this might not be suitable so you may need to consider whether to update the value of lbpack on each field before appending it to the file (or inside the operator’s new_field method).
It is also common practice to update the lbproc value of the field’s lookup header when the data has been modified (some applications use this to determine certain types of field) - you should update this value before appending the modified fields to the file if required (or do it inside the operator’s new_field method).
Example 6: Set all level-38 32 bit packed (LBPACK 21 = 2) fields to -1¶
import mule
from mule.operators import ScaleFactorOperator, AddScalarOperator
zero_operator = ScaleFactorOperator(0.0)
subtract_one_operator = AddScalarOperator(-1.0)
ff = mule.FieldsFile.from_file("InputFile")
for ifield, field in enumerate(ff.fields):
if field.lbrel in (2,3) and field.lblev == 38 and field.lbpack == 2:
ff.fields[ifield] = (
subtract_one_operator(zero_operator(field)))
ff.to_file("OutputFile")
Note
There isn’t a single built-in operator in mule.operators which provides this functionality, but it can be achieved by combining the effects of 2 operators - note the way in which they can be “chained” together to apply multiple operations.
Unlike example 1 - this example modifies the fields in the file object in-place; this is neater but overwrites the point of access to the original fields so might not always be ideal.
Notice the check on lbrel - this is to make sure only fields with valid header release numbers are processed.
The loop also filters for the required level; note that since only fields with valid header release numbers define lblev and lbpack it is important that the if conditions for these appear after the check on lbrel.
It is common practice to update the lbproc value of the field’s lookup header when the data has been modified (some applications use this to determine certain types of field) - you should update this value before appending the modified fields to the file if required (or do it inside the operator’s new_field method).
Example 7: Add random perturbations at the level of the least significant bit to all theta fields (STASH code 4)¶
import sys
import mule
import numpy as np
EPSILON = sys.float_info.epsilon
class LSBPerturbOperator(mule.DataOperator):
"""Operator which adds LSB perturbations to a field"""
def __init__(self):
pass
def new_field(self, source_field):
"""Creates the new field object"""
field = source_field.copy()
return field
def transform(self, source_field, new_field):
"""Performs the data manipulation"""
data = source_field.get_data()
# Create an array of perturbations and apply them to the data
random_numbers = (2.0*np.random.random(data.shape) - 1.0)*EPSILON
data_out = data + data*random_numbers
# If the field defines MDI, reset missing points from original
# field back to MDI in the output
if hasattr(source_field, "bmdi"):
mdi = source_field.bmdi
mask = (data == mdi)
data_out[mask] = mdi
return data_out
lsb_perturb_operator = LSBPerturbOperator()
ff = mule.FieldsFile.from_file("InputFile")
for ifield, field in enumerate(ff.fields):
if field.lbrel in (2,3) and field.lbuser4 == 4:
ff.fields[ifield] = lsb_perturb_operator(field)
ff.to_file("OutputFile")
Note
There aren’t any built-in operators in mule.operators which provide this functionality, so this example creates a custom operator.
Unlike example 1 - this example modifies the fields in the file object in-place; this is neater but overwrites the point of access to the original fields so might not always be ideal.
Notice the check on lbrel - this is to make sure only fields with valid header release numbers are processed.
The loop also filters for the required STASH code; note that since only fields with valid header release numbers define lbuser4 it is important that the if conditions for these appear after the check on lbrel.
Since the packing code (lbpack) of the modified fields is not changed Mule will try to write the fields out with the same packing method and accuracy - in some cases this might not be suitable so you may need to consider whether to update the value of lbpack on each field before appending it to the file (or inside the operator’s new_field method).
It is also common practice to update the lbproc value of the field’s lookup header when the data has been modified (some applications use this to determine certain types of field) - you should update this value before appending the modified fields to the file if required (or do it inside the operator’s new_field method).
Example 8: Add 1.0 to fields with Item Numbers 2 or 3, only outputting fields operated on¶
import mule
from mule.operators import AddScalarOperator
add_1_operator = AddScalarOperator(1.0)
ff = mule.FieldsFile.from_file("InputFile")
ff_out = ff.copy()
for field in ff.fields:
if field.lbrel in (2,3) and field.lbuser4 in (2,3):
ff_out.fields.append(add_1_operator(field))
ff_out.to_file("OutputFile")
Note
Unlike many of the other examples, this example makes a copy of the original object - this allows only the desired fields to be added and hence written out.
Notice the check on lbrel - this is to make sure only fields with valid header release numbers are processed.
The loop also filters for the required STASH codes; note that since only fields with valid header release numbers define lbuser4 it is important that the if conditions for these appear after the check on lbrel.
It is common practice to update the lbproc value of the field’s lookup header when the data has been modified (some applications use this to determine certain types of field) - you should update this value before appending the modified fields to the file if required (or do it inside the operator’s new_field method).