Skip to content

Create README.md #25

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Mar 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
**/__pycache__/
*.pyc
.pyc
.hypothesis/
**/.*_cache/
*_cache/
.vscode/
.DS_Store
.idea
.coverage
*~
*.egg-info
coverage_html
*.code-workspace
.egg-info
.code-workspace
35 changes: 35 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# UMfile_utils

UMfile_util is a Python library that provides specific tools to process [UM files](https://code.metoffice.gov.uk/doc/um/latest/papers/umdp_F03.pdf).

## List of functions

- [perturbIC](#perturbic)
- [um_fields_subset](#um-fields-subset)
- [change_date](#change-date)

### perturbIC
Apply a random perturbation to a restart file, with an optional seed to control the random generation.
This can be useful for simulations that fail due to random divergence or for generating multiple ensemble members in climate experiments.

Run `pertubIC --help` for detailed usage information.

### um_fields_subset

Subset a UM file to generate an output containing only selected fields. Options are available to include or exclude specific STASH variables.

Run `um_fields_subset --help` for detailed usage information.

### change_date
Change the time metadata of a UM restart file, without modifying its data content.

Run `change_date --help` for detailed usage information

## Installation
TO BE ADDED

## Contributing
TO BE ADDED

## License
TO BE ADDED
18 changes: 13 additions & 5 deletions src/umfile_utils/change_dump_date.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import os
import mule
import argparse
from textwrap import dedent

def validate_year_value(value):
"""
Expand Down Expand Up @@ -48,10 +49,6 @@ def validate_date_value(value):
"""
Ensures the date is in YYYYMMDD format and extract year, month, and day.
"""
try:
ivalue = int(value)
except ValueError:
raise argparse.ArgumentTypeError(f"Invalid date: {value}. Must be in YYYYMMDD format.")
if len(value) != 8:
raise argparse.ArgumentTypeError(f"Invalid date: {value}. Must be exactly 8 digits (YYYYMMDD).")
year = validate_year_value(value[:4])
Expand Down Expand Up @@ -83,7 +80,18 @@ def parse_args():
argparse.Namespace
Argparse namespace containing the parsed command line arguments.
"""
parser = argparse.ArgumentParser(description="Modify UM dump file initial and valid dates")
DESCRIPTION = dedent(
"""
Change the time metadata of a UM restart file, without modifying its data content.

Examples:

1. Change date of a UM restart file to the 22nd Jan 2025
`change_dump_date /path/to/restart.dump --date 20250122`
"""
)
parser = argparse.ArgumentParser(description=DESCRIPTION, formatter_class=argparse.RawDescriptionHelpFormatter)

parser.add_argument('ifile', metavar="INPUT_PATH", help='Path to the input file.')
parser.add_argument('-o', '--output', dest='output_path', metavar="OUTPUT_PATH",
help='Path to the output file. If omitted, the default output file is created by appending "_newdate" to the input path.')
Expand Down
23 changes: 18 additions & 5 deletions src/umfile_utils/perturbIC.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@

import os
import argparse
from textwrap import dedent
from numpy.random import PCG64, Generator
import mule

TIMESERIES_LBCODES = (31320, 31323)
STASH_THETA = 4

def parse_args():
"""
Expand All @@ -22,7 +25,21 @@ def parse_args():
args_parsed : argparse.Namespace
Argparse namespace containing the parsed command line arguments.
"""
parser = argparse.ArgumentParser(description="Perturb UM initial dump")
DESCRIPTION = dedent(
"""
Apply a random perturbation to a restart file, with an optional seed to control the random generation.
The perturbation is applied to the potential tempoerature (theta) field (STASH itemcode 4) by default.

Examples:

1. Perturbate initial conditions of an experiment that fail due to divergence
`perturbIC /path/to/restart.dump`

2. Generate initial conditions for 10 ensemble members from the same restart file
`for ens in {1..10}; do perturbIC /path/to/restart.dump -a 0.005 -s $ens -o /path/to/restart.dump_ensamble{$ens}; done`
"""
)
parser = argparse.ArgumentParser(description=DESCRIPTION, formatter_class=argparse.RawDescriptionHelpFormatter)
# Positional arguments
parser.add_argument('ifile', metavar="INPUT_PATH", help='Path to the input file.')
# Optional arguments
Expand Down Expand Up @@ -186,10 +203,6 @@ def main():
"""
Add a bi-dimensional random perturbation to the potential temperature field 'Theta' (STASH itemcode = 4) of a UM fields file.
"""

# Define all the variables
STASH_THETA = 4

# Parse the command line arguments
args = parse_args()

Expand Down
26 changes: 22 additions & 4 deletions src/umfile_utils/um_fields_subset.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
import os
import argparse
import warnings
from textwrap import dedent
from itertools import chain

PROGNOSTIC_STASH_CODES = tuple(chain(range(1,999+1), range(33001,34999+1)))

def convert_to_list(value: str):
Expand Down Expand Up @@ -51,7 +53,23 @@ def parse_args():
args_parsed : argsparse.Namespace
Argparse namespace containing the parsed command line arguments.
"""
parser = argparse.ArgumentParser(description="Subset UM fields based on user-specified options.")
DESCRIPTION = dedent(
"""
Subset a UM file to generate an output containing only selected fields.

Examples:

1. Subset UM file only including STASH codes 155, 156, 3100 and 3101
`um_fields_subset /path/to/um/file --include 155,156,3100,3101`

2. Subset UM file excluding the STASH codes 132 and 14020
`um_fields_subset /path/to/um/file --exclude 132,14020`

2. Subset UM file only including prognostic STASH codes
`um_fields_subset /path/to/um/file -p`
"""
)
parser = argparse.ArgumentParser(description=DESCRIPTION, formatter_class=argparse.RawDescriptionHelpFormatter)

# Positional arguments
parser.add_argument(dest='ifile', metavar="INPUT_PATH", help='Path to the input file.')
Expand All @@ -61,9 +79,9 @@ def parse_args():
meg.add_argument('-p', '--prognostic', dest='prognostic', action='store_true',
help="Only include prognostic variables (sections 0, 33 and 34). Cannot be used together with --include or --exclude.")
meg.add_argument('--include', dest='include_list', type=convert_to_list, metavar="STASH_CODES",
help="Comma-separated list of STASH codes to include in the output file. Any STASH code present in the input file, but not contained in this STASH code list, will not be present in the output file. Cannot be used together with --prognostic or --exclude.")
help="Comma-separated list of STASH codes to include in the output file. Any STASH code present in the input file but not contained in this STASH code list will not be present in the output file. Cannot be used together with --prognostic or --exclude.")
meg.add_argument('--exclude', dest='exclude_list', type=convert_to_list, metavar="STASH_CODES",
help="Comma-separated list of STASH codes to exclude from the output file. All STASH codes present in the input file, but not contained in this STASH code list, will be present in the output file. Cannot be used together with --prognostic or --include.")
help="Comma-separated list of STASH codes to exclude from the output file. All STASH codes present in the input file but not contained in this STASH code list will be present in the output file. Cannot be used together with --prognostic or --include.")
parser.add_argument('--validate', action='store_true',
help='Validate the output fields file using mule validation.')
# Parse arguments
Expand Down Expand Up @@ -219,4 +237,4 @@ def main():
filtered_file.to_file(output_filename)

if __name__== "__main__":
main() # pragma: no cover
main() # pragma: no cover
Loading