From 496017c7afda02966fa0959a35cdad8842440dd4 Mon Sep 17 00:00:00 2001 From: Halle Derry Date: Tue, 7 Jan 2025 16:00:21 -0500 Subject: [PATCH 01/24] make_mosaic: more detailed code outline + MosaicStruct draft - created the main program for make_mosaic with rough outline - began outlining my ctypes wrapper for get_contact and created shared library to test function access - created a draft of MosaicStruct and will make a proper unit test - xarray vs netcdf4 package: unsure which is more suitable for mosaic file creation, may benefit mor e from low-level interface, memory management, ... I can switch to xarray if needed and for uniformity. i# --- gridtools/make_mosaic/make_mosaic.py | 79 ++++++++++++++++++ gridtools/shared/MosaicStruct.py | 73 ++++++++++++++++ gridtools_lib/pylib/make_mosaic/empty.py | 1 - gridtools_lib/pylib/make_mosaic/libcontact.so | Bin 0 -> 16840 bytes .../pylib/make_mosaic/mosaic_util.py | 26 ++++++ 5 files changed, 178 insertions(+), 1 deletion(-) create mode 100755 gridtools/make_mosaic/make_mosaic.py create mode 100644 gridtools/shared/MosaicStruct.py delete mode 100644 gridtools_lib/pylib/make_mosaic/empty.py create mode 100755 gridtools_lib/pylib/make_mosaic/libcontact.so create mode 100644 gridtools_lib/pylib/make_mosaic/mosaic_util.py diff --git a/gridtools/make_mosaic/make_mosaic.py b/gridtools/make_mosaic/make_mosaic.py new file mode 100755 index 0000000..0bb8560 --- /dev/null +++ b/gridtools/make_mosaic/make_mosaic.py @@ -0,0 +1,79 @@ +import click +import sys + + +#from gridtools import MosaicStruct + +@click.command() + +@click.option('--num_tiles', type=int, help="number of tiles") +@click.option('--grid_dir') +@click.option('--global_mosaic') +@click.option('--regional_mosaic') +@click.option('--input_mosaic') +@click.option('--atmos_mosaic') +@click.option('--ocean_mosaic') +@click.option('--ocean_topog') +@click.option('--mosaic_name') +@click.option('--tile_file', '-f', multiple=True, type=click.Path(exists=True)) +@click.option('--periodx') +@click.option('--periody') +@click.option('--ocean_topog') +@click.option('--sea_level') +@click.option('--land_frac_file') +@click.option('--land_frac_field') +@click.option('--land_mosaic') +@click.option('--wave_mosaic') +@click.option('--interp_order') +@click.option('--area_ratio_thresh') +@click.option('--check') +@click.option('--print_memory') +@click.option('--rotate_poly') +@click.argument('type') + +def main(num_tiles, grid_dir, global_mosaic, regional_mosaic, input_mosaic, atmos_mosaic, ocean_mosaic, mosaic_name, tile_file, periodx, periody, ocean_topog, sea_level, land_frac_file, land_frac_field, land_mosaic, wave_mosaic, interp_order, area_ratio_thresh, check, print_memory, rotate_poly, type): + + if type == 'solo': + make_solo_mosaic(num_tiles, grid_dir, mosaic_name, tile_file, periodx, periody) + elif type == 'regional': + make_regional_mosaic(global_mosaic, regional_mosaic) + else: + make_coupler_moasic(atmos_mosaic, ocean_mosaic, ocean_topog, land_mosaic, wave_mosaic, interp_order, sea_level, mosaic_name, area_ratio_thresh, check, print_memory, rotate_poly) + + + + +def make_solo_mosaic(num_tiles, grid_dir, mosaic_name, tile_file, periodx, periody): + tilefiles = list(tile_file) + nfiles = len(tilefiles) + #if file name is not specified + if nfiles == 0: + if num_tiles == 1: + tilefiles = "horizontal_grid.nc" + else: + for n in range(num_tiles): + tilefiles.append("horizontal_grid.tile{}.nc".format(n)) + else: + #ensure that the number of tiles matches the number of grid files provided + if nfiles != num_tiles: + sys.exit("Error: number tiles provided through --ntiles does not match number of grid files passed through") + print(tilesfiles) + #gather grid data from grid files... + + #find contact regions + + #define dimension + + #define variable + + +def make_regional_mosaic(global_mosaic, regional_mosaic): + pass + +def make_coupler_mosaic(atmos_mosaic, ocean_mosaic, ocean_topog, land_mosaic, wave_mosaic, interp_order, sea_level, mosaic_name, area_ratio_thresh, check, print_memory, rotate_poly): + pass + + + +if __name__ == '__main__': + main() diff --git a/gridtools/shared/MosaicStruct.py b/gridtools/shared/MosaicStruct.py new file mode 100644 index 0000000..7669fc2 --- /dev/null +++ b/gridtools/shared/MosaicStruct.py @@ -0,0 +1,73 @@ +from netCDF4 import Dataset +from dataclasses import dataclass,field +from datetime import datetime +import socket +import time +import sys +import xarray as xr + + +@dataclass +class MosaicStruct: + output_file: str = field(default=None) + ntiles: int = field(default=None) + ncontacts: int = field(default=None) + + + def get_gridfiles(self, mosaic_file: str): + try: + mosaic = xr.open_dataset(mosaic_file) + except FileNotFoundError: + sys.exit("No such file or directory: {}".format(mosaic_file)) + res = (mosaic['gridfiles'][:]) + gridfiles = list(mosaic['gridfiles'].values.flatten()) + return gridfiles + + + def get_ntiles(self, mosaic_file: str): + try: + mosaic = xr.open_dataset(mosaic_file) + except FileNotFoundError: + sys.exit("No such file or directory: {}".format(mosaic_file)) + ntiles = mosaic['ntiles'].size + return ntiles + + + def write_out_mosaic(self): + ncfile = Dataset(self.output_file, 'w', format='NETCDF4') + + #define dimensions + ntiles_dim = ncfile.createDimension('ntiles', self.ntiles) + ncontacts_dim = ncfile.createDimension('ncontact', self.ncontacts) + string_dim = ncfile.createDimension('string', 255) + + + #define variables + mosaic = ncfile.createVariable('mosaic', 'S1', ('string')) + mosaic.setncatts({"standard_name": "grid_mosaic_spec", "contact_regions": "contacts", "children": "gridtiles", "grid_descriptor": ""}) + + gridlocation = ncfile.createVariable('gridlocation', 'S1', ('string')) + gridlocation.standard_name = 'grid_file_location' + + + gridfiles = ncfile.createVariable('gridfiles', 'S1', ('ntiles', 'string')) + gridtiles = ncfile.createVariable('gridtiles', 'S1', ('ntiles', 'string')) + + + contacts = ncfile.createVariable('contacts', 'S1', ('ncontact', 'string')) + contacts.setncatts({"standard_name": "grid_contact_spec", "contact_type": "boundary", "alignment": "true", "contact_index": "contact_index", "orientation": "orient"}) + + + contactidx = ncfile.createVariable('contact_index', 'S1', ('ncontact', 'string')) + contactidx.standard_name = 'starting_ending_point_index_of_contact' + + #global attributes + ncfile.grid_version = '0.2' + ncfile.code_release_verison = '' + ncfile.git_hash = '' + ncfile.creationtime = time.ctime(time.time()) + ncfile.hostname = socket.gethostname() + ncfile.history = '' + + + ncfile.close() diff --git a/gridtools_lib/pylib/make_mosaic/empty.py b/gridtools_lib/pylib/make_mosaic/empty.py deleted file mode 100644 index e517126..0000000 --- a/gridtools_lib/pylib/make_mosaic/empty.py +++ /dev/null @@ -1 +0,0 @@ -# For git tracking, to be removed \ No newline at end of file diff --git a/gridtools_lib/pylib/make_mosaic/libcontact.so b/gridtools_lib/pylib/make_mosaic/libcontact.so new file mode 100755 index 0000000000000000000000000000000000000000..fd31433785eeace205a01583f867110079842172 GIT binary patch literal 16840 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 NiBuQoITRd8@xSwand1Ne literal 0 HcmV?d00001 diff --git a/gridtools_lib/pylib/make_mosaic/mosaic_util.py b/gridtools_lib/pylib/make_mosaic/mosaic_util.py new file mode 100644 index 0000000..6313ba2 --- /dev/null +++ b/gridtools_lib/pylib/make_mosaic/mosaic_util.py @@ -0,0 +1,26 @@ +import ctypes + + + +def get_align_contact(): + + #generated shared library from c code + #use CDLL function to load the shared library file, returns library object that can be used tto access functions + clibrary = ctypes.CDLL('libcontact.so') + + #acquire function signature + get_align_contact = clibrary.get_align_contact + + #represent parameters needed + get_align_contact.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_double), ctypes.POINTER(ctypes.c_double), ctypes.POINTER(ctypes.c_double), ctypes.POINTER(ctypes.c_double), ctypes.c_double, ctypes.c_double, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int)] + + #specify type of return value + get_align_contact.restype = ctypes.c_int + + #create ctype doubles/ints from inputs + + count = get_align_contact() + + return count + + From 858da6153b7fcdc7119b8d2a99992fd0ff5aebff Mon Sep 17 00:00:00 2001 From: Halle Derry Date: Wed, 15 Jan 2025 13:25:24 -0500 Subject: [PATCH 02/24] -Modified ctypes wrapper to import as ct and aliased ctypes.c_int -Removed contact library -fixed typo in make_mosaic.py MosaicObj -Switched from netcdf4 to xarray package -used post_init to populate gridfiles and ntiles -> read_mosaic method & created file pointer for future use -implemented class method for generating grid dictionary -> need to work on making nested dict accessible --- gridtools/make_mosaic/make_mosaic.py | 2 +- gridtools/shared/MosaicStruct.py | 73 ------------------- gridtools/shared/mosaicobj.py | 71 ++++++++++++++++++ .../pylib/make_mosaic/mosaic_util.py | 7 +- 4 files changed, 76 insertions(+), 77 deletions(-) delete mode 100644 gridtools/shared/MosaicStruct.py create mode 100644 gridtools/shared/mosaicobj.py diff --git a/gridtools/make_mosaic/make_mosaic.py b/gridtools/make_mosaic/make_mosaic.py index 0bb8560..39549dc 100755 --- a/gridtools/make_mosaic/make_mosaic.py +++ b/gridtools/make_mosaic/make_mosaic.py @@ -38,7 +38,7 @@ def main(num_tiles, grid_dir, global_mosaic, regional_mosaic, input_mosaic, atmo elif type == 'regional': make_regional_mosaic(global_mosaic, regional_mosaic) else: - make_coupler_moasic(atmos_mosaic, ocean_mosaic, ocean_topog, land_mosaic, wave_mosaic, interp_order, sea_level, mosaic_name, area_ratio_thresh, check, print_memory, rotate_poly) + make_coupler_mosaic(atmos_mosaic, ocean_mosaic, ocean_topog, land_mosaic, wave_mosaic, interp_order, sea_level, mosaic_name, area_ratio_thresh, check, print_memory, rotate_poly) diff --git a/gridtools/shared/MosaicStruct.py b/gridtools/shared/MosaicStruct.py deleted file mode 100644 index 7669fc2..0000000 --- a/gridtools/shared/MosaicStruct.py +++ /dev/null @@ -1,73 +0,0 @@ -from netCDF4 import Dataset -from dataclasses import dataclass,field -from datetime import datetime -import socket -import time -import sys -import xarray as xr - - -@dataclass -class MosaicStruct: - output_file: str = field(default=None) - ntiles: int = field(default=None) - ncontacts: int = field(default=None) - - - def get_gridfiles(self, mosaic_file: str): - try: - mosaic = xr.open_dataset(mosaic_file) - except FileNotFoundError: - sys.exit("No such file or directory: {}".format(mosaic_file)) - res = (mosaic['gridfiles'][:]) - gridfiles = list(mosaic['gridfiles'].values.flatten()) - return gridfiles - - - def get_ntiles(self, mosaic_file: str): - try: - mosaic = xr.open_dataset(mosaic_file) - except FileNotFoundError: - sys.exit("No such file or directory: {}".format(mosaic_file)) - ntiles = mosaic['ntiles'].size - return ntiles - - - def write_out_mosaic(self): - ncfile = Dataset(self.output_file, 'w', format='NETCDF4') - - #define dimensions - ntiles_dim = ncfile.createDimension('ntiles', self.ntiles) - ncontacts_dim = ncfile.createDimension('ncontact', self.ncontacts) - string_dim = ncfile.createDimension('string', 255) - - - #define variables - mosaic = ncfile.createVariable('mosaic', 'S1', ('string')) - mosaic.setncatts({"standard_name": "grid_mosaic_spec", "contact_regions": "contacts", "children": "gridtiles", "grid_descriptor": ""}) - - gridlocation = ncfile.createVariable('gridlocation', 'S1', ('string')) - gridlocation.standard_name = 'grid_file_location' - - - gridfiles = ncfile.createVariable('gridfiles', 'S1', ('ntiles', 'string')) - gridtiles = ncfile.createVariable('gridtiles', 'S1', ('ntiles', 'string')) - - - contacts = ncfile.createVariable('contacts', 'S1', ('ncontact', 'string')) - contacts.setncatts({"standard_name": "grid_contact_spec", "contact_type": "boundary", "alignment": "true", "contact_index": "contact_index", "orientation": "orient"}) - - - contactidx = ncfile.createVariable('contact_index', 'S1', ('ncontact', 'string')) - contactidx.standard_name = 'starting_ending_point_index_of_contact' - - #global attributes - ncfile.grid_version = '0.2' - ncfile.code_release_verison = '' - ncfile.git_hash = '' - ncfile.creationtime = time.ctime(time.time()) - ncfile.hostname = socket.gethostname() - ncfile.history = '' - - - ncfile.close() diff --git a/gridtools/shared/mosaicobj.py b/gridtools/shared/mosaicobj.py new file mode 100644 index 0000000..e564c36 --- /dev/null +++ b/gridtools/shared/mosaicobj.py @@ -0,0 +1,71 @@ +from typing import Optional, Dict +from dataclasses import dataclass,field +from datetime import datetime +import socket +import time +import sys +import xarray as xr +import numpy as np +import numpy.typing as npt + +from gridtools_lib import GridObj + +@dataclass +class MosaicObj: + mosaic_file: str = None + output_file: str = None + ntiles: int = None + ncontact: int = None + mosaic_name: str = None + gridlocation: str = None + gridfiles: npt.NDArray[np.str_] = None + gridtiles: npt.NDArray[np.str_] = None + contacts: npt.NDArray[np.str_] = None + contact_index: npt.NDArray[np.str_] = None + mosaic: object = field(init=False) + grid_dict: Optional[Dict] | None = field(default_factory=dict) + + def __post_init__(self): + try: + if self.mosaic_file is not None: + self.mosaic = xr.open_dataset(self.mosaic_file) + except FileNotFoundError: + sys.exit("No such file or directory: {}".format(self.mosaic_file)) + + if self.gridfiles is None and self.ntiles is None: + self.gridfiles, self.ntiles = self.read_mosaic() + + + def read_mosaic(self): + files = list(self.mosaic['gridfiles'][:].values.flatten()) + gridfiles = [f.decode('utf-8') for f in files] + ntiles = self.mosaic['ntiles'].size + + return gridfiles, ntiles + + def griddict(self): + #parse gridfiles + i = 1 + for file in self.gridfiles: + self.grid_dict[f'tile{i}'] = GridObj.from_file(file) + i+=1 + + def write_out_mosaic(self): + #mosaic_vars = dict() + + mosaic = xr.DataArray([self.mosaic_name], attrs=dict(standard_name="grid_mosaic_spec", contact_regions="contacts", children="gridtiles", grid_descriptor="")) + + gridlocation = xr.DataArray([self.gridlocation], attrs=dict(standard_name="grid_file_location")) + + gridfiles = xr.DataArray(data=self.gridfiles, dims=["ntiles"]) + + gridtiles = xr.DataArray(data=self.gridtiles, dims=["ntiles"]) + + contacts = xr.DataArray(data=self.contacts, dims=["ncontact"], attrs=dict(standard_name="grid_contact_spec", contact_type="boundary", alignment="true", contact_index="contact_index", orientation="orient")) + + contact_index = xr.DataArray(data=self.contact_index, dims=["ncontact"], attrs=dict(standard_name="starting_ending_point_index_of_contact")) + + out = xr.Dataset(data_vars={ "mosaic": mosaic, "gridlocation": gridlocation, "gridfiles": gridfiles, "gridtiles": gridtiles, "contacts": contacts, "contact_index": contact_index}) + + out.to_netcdf(self.output_file) + diff --git a/gridtools_lib/pylib/make_mosaic/mosaic_util.py b/gridtools_lib/pylib/make_mosaic/mosaic_util.py index 6313ba2..6ad1775 100644 --- a/gridtools_lib/pylib/make_mosaic/mosaic_util.py +++ b/gridtools_lib/pylib/make_mosaic/mosaic_util.py @@ -1,18 +1,19 @@ -import ctypes +import ctypes as ct def get_align_contact(): + size1_t = ct.c_int #generated shared library from c code #use CDLL function to load the shared library file, returns library object that can be used tto access functions - clibrary = ctypes.CDLL('libcontact.so') + clibrary = ct.CDLL('libcontact.so') #acquire function signature get_align_contact = clibrary.get_align_contact #represent parameters needed - get_align_contact.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_double), ctypes.POINTER(ctypes.c_double), ctypes.POINTER(ctypes.c_double), ctypes.POINTER(ctypes.c_double), ctypes.c_double, ctypes.c_double, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int)] + get_align_contact.argtypes = [size1_t, size1_t, size1_t, size1_t, size1_t, size1_t, ct.POINTER(ctypes.ct_double), ct.POINTER(ct.c_double), ct.POINTER(ct.c_double), ct.POINTER(ct.c_double), ct.c_double, ct.c_double, ct.POINTER(size1_t), ct.POINTER(size1_t), ct.POINTER(size1_t), ct.POINTER(size1_t), ct.POINTER(size1_t), ct.POINTER(size1_t), ct.POINTER(size1_t), ct.POINTER(size1_t)] #specify type of return value get_align_contact.restype = ctypes.c_int From be320746e1a6f01ef70ef5e879a848ba289fb440 Mon Sep 17 00:00:00 2001 From: Halle Derry Date: Wed, 15 Jan 2025 13:40:38 -0500 Subject: [PATCH 03/24] remove contact library -> libcontact.so --- gridtools_lib/pylib/make_mosaic/libcontact.so | Bin 16840 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100755 gridtools_lib/pylib/make_mosaic/libcontact.so diff --git a/gridtools_lib/pylib/make_mosaic/libcontact.so b/gridtools_lib/pylib/make_mosaic/libcontact.so deleted file mode 100755 index fd31433785eeace205a01583f867110079842172..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16840 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 NiBuQoITRd8@xSwand1Ne From f441fb01d7dac5c1a160add8cc63a7bf81ba9d8f Mon Sep 17 00:00:00 2001 From: Halle Derry Date: Wed, 22 Jan 2025 12:00:50 -0500 Subject: [PATCH 04/24] removing drafts --- gridtools/make_mosaic/make_mosaic.py | 79 ------------------- .../pylib/make_mosaic/mosaic_util.py | 27 ------- 2 files changed, 106 deletions(-) delete mode 100755 gridtools/make_mosaic/make_mosaic.py delete mode 100644 gridtools_lib/pylib/make_mosaic/mosaic_util.py diff --git a/gridtools/make_mosaic/make_mosaic.py b/gridtools/make_mosaic/make_mosaic.py deleted file mode 100755 index 39549dc..0000000 --- a/gridtools/make_mosaic/make_mosaic.py +++ /dev/null @@ -1,79 +0,0 @@ -import click -import sys - - -#from gridtools import MosaicStruct - -@click.command() - -@click.option('--num_tiles', type=int, help="number of tiles") -@click.option('--grid_dir') -@click.option('--global_mosaic') -@click.option('--regional_mosaic') -@click.option('--input_mosaic') -@click.option('--atmos_mosaic') -@click.option('--ocean_mosaic') -@click.option('--ocean_topog') -@click.option('--mosaic_name') -@click.option('--tile_file', '-f', multiple=True, type=click.Path(exists=True)) -@click.option('--periodx') -@click.option('--periody') -@click.option('--ocean_topog') -@click.option('--sea_level') -@click.option('--land_frac_file') -@click.option('--land_frac_field') -@click.option('--land_mosaic') -@click.option('--wave_mosaic') -@click.option('--interp_order') -@click.option('--area_ratio_thresh') -@click.option('--check') -@click.option('--print_memory') -@click.option('--rotate_poly') -@click.argument('type') - -def main(num_tiles, grid_dir, global_mosaic, regional_mosaic, input_mosaic, atmos_mosaic, ocean_mosaic, mosaic_name, tile_file, periodx, periody, ocean_topog, sea_level, land_frac_file, land_frac_field, land_mosaic, wave_mosaic, interp_order, area_ratio_thresh, check, print_memory, rotate_poly, type): - - if type == 'solo': - make_solo_mosaic(num_tiles, grid_dir, mosaic_name, tile_file, periodx, periody) - elif type == 'regional': - make_regional_mosaic(global_mosaic, regional_mosaic) - else: - make_coupler_mosaic(atmos_mosaic, ocean_mosaic, ocean_topog, land_mosaic, wave_mosaic, interp_order, sea_level, mosaic_name, area_ratio_thresh, check, print_memory, rotate_poly) - - - - -def make_solo_mosaic(num_tiles, grid_dir, mosaic_name, tile_file, periodx, periody): - tilefiles = list(tile_file) - nfiles = len(tilefiles) - #if file name is not specified - if nfiles == 0: - if num_tiles == 1: - tilefiles = "horizontal_grid.nc" - else: - for n in range(num_tiles): - tilefiles.append("horizontal_grid.tile{}.nc".format(n)) - else: - #ensure that the number of tiles matches the number of grid files provided - if nfiles != num_tiles: - sys.exit("Error: number tiles provided through --ntiles does not match number of grid files passed through") - print(tilesfiles) - #gather grid data from grid files... - - #find contact regions - - #define dimension - - #define variable - - -def make_regional_mosaic(global_mosaic, regional_mosaic): - pass - -def make_coupler_mosaic(atmos_mosaic, ocean_mosaic, ocean_topog, land_mosaic, wave_mosaic, interp_order, sea_level, mosaic_name, area_ratio_thresh, check, print_memory, rotate_poly): - pass - - - -if __name__ == '__main__': - main() diff --git a/gridtools_lib/pylib/make_mosaic/mosaic_util.py b/gridtools_lib/pylib/make_mosaic/mosaic_util.py deleted file mode 100644 index 6ad1775..0000000 --- a/gridtools_lib/pylib/make_mosaic/mosaic_util.py +++ /dev/null @@ -1,27 +0,0 @@ -import ctypes as ct - - - -def get_align_contact(): - - size1_t = ct.c_int - #generated shared library from c code - #use CDLL function to load the shared library file, returns library object that can be used tto access functions - clibrary = ct.CDLL('libcontact.so') - - #acquire function signature - get_align_contact = clibrary.get_align_contact - - #represent parameters needed - get_align_contact.argtypes = [size1_t, size1_t, size1_t, size1_t, size1_t, size1_t, ct.POINTER(ctypes.ct_double), ct.POINTER(ct.c_double), ct.POINTER(ct.c_double), ct.POINTER(ct.c_double), ct.c_double, ct.c_double, ct.POINTER(size1_t), ct.POINTER(size1_t), ct.POINTER(size1_t), ct.POINTER(size1_t), ct.POINTER(size1_t), ct.POINTER(size1_t), ct.POINTER(size1_t), ct.POINTER(size1_t)] - - #specify type of return value - get_align_contact.restype = ctypes.c_int - - #create ctype doubles/ints from inputs - - count = get_align_contact() - - return count - - From fce58f5dc46f46ec2fee45681148ff0258d47d10 Mon Sep 17 00:00:00 2001 From: Halle Derry Date: Wed, 5 Feb 2025 12:19:22 -0500 Subject: [PATCH 05/24] delete .orig after merge --- FMSgridtools/shared/mosaicobj.py.orig | 141 -------------------------- 1 file changed, 141 deletions(-) delete mode 100644 FMSgridtools/shared/mosaicobj.py.orig diff --git a/FMSgridtools/shared/mosaicobj.py.orig b/FMSgridtools/shared/mosaicobj.py.orig deleted file mode 100644 index d252918..0000000 --- a/FMSgridtools/shared/mosaicobj.py.orig +++ /dev/null @@ -1,141 +0,0 @@ -from typing import Optional, Dict -from dataclasses import dataclass,field -<<<<<<< HEAD:gridtools/shared/mosaicobj.py -from datetime import datetime -import socket -import time -import sys -import xarray as xr -import numpy as np -import numpy.typing as npt - -from gridtools_lib import GridObj -======= -import xarray as xr -import numpy as np -import numpy.typing as npt -from gridtools.shared import GridObj -from gridtools.shared.gridtools_utils import check_file_is_there ->>>>>>> upstream/main:FMSgridtools/shared/mosaicobj.py - -@dataclass -class MosaicObj: - mosaic_file: str = None - output_file: str = None - ntiles: int = None - ncontact: int = None - mosaic_name: str = None - gridlocation: str = None - gridfiles: npt.NDArray[np.str_] = None - gridtiles: npt.NDArray[np.str_] = None - contacts: npt.NDArray[np.str_] = None - contact_index: npt.NDArray[np.str_] = None -<<<<<<< HEAD:gridtools/shared/mosaicobj.py - mosaic: object = field(init=False) - grid_dict: Optional[Dict] | None = field(default_factory=dict) - - def __post_init__(self): - try: - if self.mosaic_file is not None: - self.mosaic = xr.open_dataset(self.mosaic_file) - except FileNotFoundError: - sys.exit("No such file or directory: {}".format(self.mosaic_file)) - - if self.gridfiles is None and self.ntiles is None: - self.gridfiles, self.ntiles = self.read_mosaic() - - - def read_mosaic(self): - files = list(self.mosaic['gridfiles'][:].values.flatten()) - gridfiles = [f.decode('utf-8') for f in files] - ntiles = self.mosaic['ntiles'].size - - return gridfiles, ntiles - - def griddict(self): - #parse gridfiles - i = 1 - for file in self.gridfiles: - self.grid_dict[f'tile{i}'] = GridObj.from_file(file) - i+=1 - - def write_out_mosaic(self): - #mosaic_vars = dict() - - mosaic = xr.DataArray([self.mosaic_name], attrs=dict(standard_name="grid_mosaic_spec", contact_regions="contacts", children="gridtiles", grid_descriptor="")) - - gridlocation = xr.DataArray([self.gridlocation], attrs=dict(standard_name="grid_file_location")) - - gridfiles = xr.DataArray(data=self.gridfiles, dims=["ntiles"]) - - gridtiles = xr.DataArray(data=self.gridtiles, dims=["ntiles"]) - - contacts = xr.DataArray(data=self.contacts, dims=["ncontact"], attrs=dict(standard_name="grid_contact_spec", contact_type="boundary", alignment="true", contact_index="contact_index", orientation="orient")) - - contact_index = xr.DataArray(data=self.contact_index, dims=["ncontact"], attrs=dict(standard_name="starting_ending_point_index_of_contact")) - - out = xr.Dataset(data_vars={ "mosaic": mosaic, "gridlocation": gridlocation, "gridfiles": gridfiles, "gridtiles": gridtiles, "contacts": contacts, "contact_index": contact_index}) -======= - dataset: object = field(init=False) - grid_dict: Optional[Dict] | None = field(default_factory=dict) - - def __post_init__(self): - if self.mosaic_file is not None and self.gridfiles is None: - check_file_is_there(self.mosaic_file) - self.dataset = xr.open_dataset(self.mosaic_file) - - self.gridfiles = self.get_gridfiles() - - def get_gridfiles(self): - try: - return [ifile.decode('ascii') for ifile in self.dataset.gridfiles.values] - except AttributeError: - print("Error: Mosaic file not provided as an attribute, unable to return gridfiles") - - def get_ntiles(self): - try: - return self.dataset.sizes['ntiles'] - except AttributeError: - print("Error: Mosaic file not provided as an attribute, unable to return number of tiles") - - def griddict(self): - gridtiles = [tile.decode('ascii') for tile in self.dataset.gridtiles.values] - for i in range(self.get_ntiles()): - self.grid_dict[gridtiles[i]] = GridObj.from_file(self.gridfiles[i]) - - def write_out_mosaic(self): - - mosaic = xr.DataArray( - [self.mosaic_name], - attrs=dict(standard_name="grid_mosaic_spec", contact_regions="contacts", - children="gridtiles", grid_descriptor="")) - - gridlocation = xr.DataArray( - [self.gridlocation], attrs=dict(standard_name="grid_file_location")) - - gridfiles = xr.DataArray( - data=self.gridfiles, dims=["ntiles"]) - - gridtiles = xr.DataArray( - data=self.gridtiles, dims=["ntiles"]) - - contacts = xr.DataArray( - data=self.contacts, dims=["ncontact"], - attrs=dict(standard_name="grid_contact_spec", contact_type="boundary", - alignment="true", contact_index="contact_index", orientation="orient")) - - contact_index = xr.DataArray( - data=self.contact_index, dims=["ncontact"], - attrs=dict(standard_name="starting_ending_point_index_of_contact")) - - out = xr.Dataset( - data_vars={"mosaic": mosaic, - "gridlocation": gridlocation, - "gridfiles": gridfiles, - "gridtiles": gridtiles, - "contacts": contacts, - "contact_index": contact_index}) ->>>>>>> upstream/main:FMSgridtools/shared/mosaicobj.py - - out.to_netcdf(self.output_file) - From 9c075d8e0f9ce87baa22fb2b97e0c740d19a0d4e Mon Sep 17 00:00:00 2001 From: hderry Date: Tue, 25 Feb 2025 16:42:19 -0500 Subject: [PATCH 06/24] -solo_mosaic and regional_mosaic functions are completed (may require additional changes in the future) -new directory for make_mosaic under pyfrenctools and ctypes wrapper for make_solo_mosaic (need to pull from pyFMS) -inital test file for mosaicobj and make_mosaic --- FMSgridtools/make_mosaic/empty.py | 1 - FMSgridtools/make_mosaic/make_mosaic.py | 289 ++++++++++++++++++ .../pyfrenctools/make_mosaic/mosaic_util.py | 79 +++++ tests/test_mosaic.py | 40 +++ 4 files changed, 408 insertions(+), 1 deletion(-) delete mode 100644 FMSgridtools/make_mosaic/empty.py create mode 100755 FMSgridtools/make_mosaic/make_mosaic.py create mode 100644 FREnctools_lib/pyfrenctools/make_mosaic/mosaic_util.py create mode 100644 tests/test_mosaic.py diff --git a/FMSgridtools/make_mosaic/empty.py b/FMSgridtools/make_mosaic/empty.py deleted file mode 100644 index e517126..0000000 --- a/FMSgridtools/make_mosaic/empty.py +++ /dev/null @@ -1 +0,0 @@ -# For git tracking, to be removed \ No newline at end of file diff --git a/FMSgridtools/make_mosaic/make_mosaic.py b/FMSgridtools/make_mosaic/make_mosaic.py new file mode 100755 index 0000000..d9e4a98 --- /dev/null +++ b/FMSgridtools/make_mosaic/make_mosaic.py @@ -0,0 +1,289 @@ +import click +import sys +import xarray as xr +import numpy as np +from FMSgridtools.shared.gridobj import GridObj +from FMSgridtools.shared.mosaicobj import MosaicObj + +ATMOS_MOSAIC_HELP = "specify the atmosphere mosaic information \ + This file contains list of tile files which specify \ + the grid information for each tile. Each grid is \ + required to be logically rectangular grid. The \ + file name cannot be 'mosaic.nc'" + +TILE_FILE_HELP = "Grid file name of all tiles in the mosaic. \ + The file name should be the \ + relative path (exclude the absolute file path) \ + The absolute file path will be dir/tile_file. \ + the default for tile_file will be'horizontal_grid.tile#.nc'" + + + + +@click.option('--num_tiles', + type=int, + help="Number of tiles in the mosaic.") + +@click.option('--dir', + type=click.Path(exists=True, file_okay=False), + help="the directory that contains all the tile grid files." ) + +@click.option('--global_mosaic', + type=click.Path(exists=True), + help="global_mosaic Specify the mosaic file for the global grid.") + +@click.option('--regional_file', + type=click.Path(exists=True), + help="regional_file Specify the regional model output file.") + +@click.option('--input_mosaic', + type=click.Path(exists=True)) + +@click.option('--atmos_mosaic', + type=click.Path(exists=True), + help = ATMOS_MOSAIC_HELP) + +@click.option('--ocean_mosaic', + type=click.Path(exists=True)) + +@click.option('--ocean_topog', + type=click.Path(exists=True)) + +@click.option('--mosaic_name', default='mosaic', + help="mosaic name; The output file will be mosaic_name.nc.") + +@click.option('--tile_file', '-f', + multiple=True, type=click.Path(exists=True, file_okay=True), + help = TILE_FILE_HELP) + +@click.option('--periodx', + default=0, + help = "Specify the period in x-direction of mosaic. \ + Default value is 0 (not periodic)") + +@click.option('--periody', default=0, + help="Specify the period in y-direction of mosaic. \ + Default value is 0 (not periodic)") + +@click.option('--sea_level', type=int, default=0) + +@click.option('--land_mosaic', type=click.Path(exists=True)) + +@click.option('--wave_mosaic', type=click.Path(exists=True)) + +@click.option('--interp_order', default=2) + +@click.option('--area_ratio_thresh', type=float, default=1e-6) + +@click.option('--check') + +@click.option('--print_memory') + +@click.option('--rotate_poly') + +@click.argument('type') + +@click.command() +def main( + num_tiles, + dir, + mosaic_name, + tile_file, + periodx, + periody, + global_mosaic, + regional_file, + input_mosaic, + atmos_mosaic, + ocean_mosaic, + ocean_topog, + land_mosaic, + wave_mosaic, + interp_order, + area_ratio_thresh, + check, + sea_level, + print_memory, + rotate_poly, + type) -> None: + """main function""" + + if type == 'solo': + make_solo_mosaic( + num_tiles, dir, mosaic_name, + tile_file, periodx, + periody) + elif type == 'regional': + make_regional_mosaic( + global_mosaic, + regional_file) + else: + make_coupler_mosaic( + atmos_mosaic, + ocean_mosaic, + ocean_topog, + land_mosaic, + wave_mosaic, + interp_order, + sea_level, + mosaic_name, + area_ratio_thresh, + check, + print_memory, + rotate_poly) + +def make_solo_mosaic( + num_tiles, + dir, + mosaic_name, + tile_file, + periodx, + periody): + """Generates mosaic information between tiles. The mosaic, information includes: + list of tile files, list of contact region, + specified by index, contact type.""" + + contacts = [] + contact_index = [] + tilefiles = list(tile_file) + nfiles = len(tilefiles) + + if nfiles == 0: + if num_tiles == 1: + tilefiles.append("horizontal_grid.tile1.nc") + else: + for n in range(num_tiles): + tilefiles.append(f"horizontal_grid.tile{n}.nc") + else: + if nfiles != num_tiles: + sys.exit("Error: number tiles provided through --ntiles" + "does not match number of grid files passed through") + + gridtiles = [f'tile{i}' for i in range(1,nfiles+1)] + + + mosaic = MosaicObj(ntiles=num_tiles, gridfiles = np.asarray(tilefiles), + gridtiles = np.asarray(gridtiles)) + + mosaic.griddict() + grid_data = {} + for tile in gridtiles: + grid_data[tile] = {} + xvals = mosaic.grid_dict[tile].x + grid_data[tile]['x'] = xvals + yvals = mosaic.grid_dict[tile].y + grid_data[tile]['y'] = yvals + nxp = mosaic.grid_dict[tile].x.shape + grid_data[tile]['nxp'] = nxp[1] + nyp = mosaic.grid_dict[tile].y.shape + grid_data[tile]['nyp'] = nyp[0] + + ncontact = 0 + #FIND CONTACT REGIONS + for n in range(1,num_tiles): + for m in range(n,num_tiles+1): + contact = Contact(n, m, grid_data[f'tile{n}']['nxp'], + grid_data[f'tile{m}']['nxp'], + grid_data[f'tile{n}']['nyp'], + grid_data[f'tile{m}']['nyp'], + grid_data[f'tile{n}']['x'], + grid_data[f'tile{m}']['x'], + grid_data[f'tile{n}']['y'], + grid_data[f'tile{m}']['y'], + periodx, periody) + count, istart1, iend1, jstart1, jend1, istart2, iend2, jstart2, jend2 = contact.align_contact(lib_file) + + if count > 0: + contacts.append(f"{mosaic_name}:tile{n}::{mosaic_name}:tile{m}") + ncontact+=1 + + tile1_istart = istart1 + tile1_iend = iend1 + tile1_jstart = jstart1 + tile1_jend = jend1 + tile2_istart = istart2 + tile2_iend = iend2 + tile2_jstart = jstart2 + tile2_jend = jend2 + + contact_index.append(f"{tile1_istart}:{tile1_iend},{tile1_jstart}:{tile1_jend}::{tile2_istart}:{tile2_iend},{tile2_jstart}:{tile2_jend}") + + print("\nCongratulations: You have successfully run solo mosaic") + print(f"NOTE: There are {ncontact} contacts\n") + + if ncontact > 0: + mosaic = MosaicObj(mosaic_name, + gridlocation=dir, + gridfiles=tilefiles, + gridtiles=np.asarray(gridtiles), + contacts=np.asarray(contacts), + contact_index=np.asarray(contact_index)) + mosaic.write_out_mosaic(f'{mosaic_name}.nc') + +def make_regional_mosaic( + global_mosaic, + regional_file): + """ Generates a horizontal grid and solo mosaic for a regional output. + The created grid and solo mosaic could be used to regrid regional + output data onto regular lat-lon grid.""" + + #get tile number from regional file + tile = int(list(filter(str.isdigit, regional_file))[0]) + + ds = xr.open_dataset(regional_file) + nx = ds.sizes['grid_xt_sub01'] + ny = ds.sizes['grid_yt_sub01'] + + indx = ds.grid_xt_sub01.values + indy = ds.grid_yt_sub01.values + + i_min = np.min(indx) + i_max = np.max(indx) + j_min = np.min(indy) + j_max = np.max(indy) + + if i_max-i_min+1 != nx: + print("Error: make_regional_mosaic: i_max-i_min+1 != nx") + if j_max-j_min+1 != ny: + print("Error: make_regional_mosaic: j_max-j_min+1 != ny") + + global_m = MosaicObj(global_mosaic) + global_m.griddict() + xt = global_m.grid_dict[f'tile{tile}'].x + xarr = xt[round(2*j_min - 2):round(2*j_min - 2 + 2*ny+1), round(2*i_min - 2):round(2*i_min - 2 + 2*nx+1)] + yt = global_m.grid_dict[f'tile{tile}'].y + yarr = yt[round(2*j_min - 2):round(2*j_min - 2 + 2*ny+1), round(2*i_min - 2):round(2*i_min - 2 + 2*nx+1)] + + tile = xr.DataArray(data=f'tile{tile}', attrs=dict(standard_name="grid_tile_spec")).astype('|S255') + x = xr.DataArray(data=xarr, dims=["nyp", "nxp"], attrs=dict( + standard_name="geographic_longitude", units="degree_east")) + y = xr.DataArray(data=yarr, dims=["nyp", "nxp"], attrs=dict( + standard_name="geographic_latitude", units="degree_north")) + + out_grid = xr.Dataset(data_vars={"tile": tile, "x": x, "y": y}) + + grid = GridObj(out_grid) + grid.write_out_grid(f"regional_grid.tile{tile}.nc") + + print("\nCongratulations: You have successfully run make_regional_mosaic") + +def make_coupler_mosaic( + atmos_mosaic, + ocean_mosaic, + ocean_topog, + land_mosaic, + wave_mosaic, + interp_order, + sea_level, + mosaic_name, + area_ratio_thresh, + check, + print_memory, + rotate_poly): + + + pass + + +if __name__ == '__main__': + main() diff --git a/FREnctools_lib/pyfrenctools/make_mosaic/mosaic_util.py b/FREnctools_lib/pyfrenctools/make_mosaic/mosaic_util.py new file mode 100644 index 0000000..c75bf5b --- /dev/null +++ b/FREnctools_lib/pyfrenctools/make_mosaic/mosaic_util.py @@ -0,0 +1,79 @@ +import ctypes +import dataclasses +import numpy as np +import numpy.typing as npt +from typing import Optional + +@dataclasses.dataclass +class Contact: + tile1: Optional[int] = None + tile2: Optional[int] = None + nxp1: Optional[int] = None + nxp2: Optional[int] = None + nyp1: Optional[int] = None + nyp2: Optional[int] = None + x1: Optional[npt.NDArray] = None + x2: Optional[npt.NDArray] = None + y1: Optional[npt.NDArray] = None + y2: Optional[npt.NDArray] = None + periodx: Optional[int] = None + periody: Optional[int] = None + + def align_contact(self, lib_file:str) -> int: + + clibrary = ctypes.CDLL(lib_file) + + #acquire function signature + find_align = clibrary.get_align_contact + + #represent parameters needed + find_align.argtypes = [ctypes.c_int, ctypes.c_int, + ctypes.c_int, ctypes.c_int, + ctypes.c_int, ctypes.c_int, + ctypes.POINTER(ctypes.c_double), + ctypes.POINTER(ctypes.c_double), + ctypes.POINTER(ctypes.c_double), + ctypes.POINTER(ctypes.c_double), + ctypes.c_double, + ctypes.c_double, + ctypes.POINTER(ctypes.c_int), + ctypes.POINTER(ctypes.c_int), + ctypes.POINTER(ctypes.c_int), + ctypes.POINTER(ctypes.c_int), + ctypes.POINTER(ctypes.c_int), + ctypes.POINTER(ctypes.c_int), + ctypes.POINTER(ctypes.c_int), + ctypes.POINTER(ctypes.c_int)] + + c_double_p = ctypes.POINTER(ctypes.c_double) + + istart1, iend1, jstart1, jend1, \ + istart2, iend2, jstart2, jend2 = [ctypes.c_int() for i in range(8)] + count = find_align( + ctypes.c_int(self.tile1), + ctypes.c_int(self.tile2), + ctypes.c_int(self.nxp1), + ctypes.c_int(self.nyp1), + ctypes.c_int(self.nxp2), + ctypes.c_int(self.nyp2), + self.x1.ctypes.data_as(c_double_p), + self.y1.ctypes.data_as(c_double_p), + self.x2.ctypes.data_as(c_double_p), + self.y2.ctypes.data_as(c_double_p), + ctypes.c_double(self.periodx), + ctypes.c_double(self.periody), + ctypes.byref(istart1), + ctypes.byref(iend1), + ctypes.byref(jstart1), + ctypes.byref(jend1), + ctypes.byref(istart2), + ctypes.byref(iend2), + ctypes.byref(jstart2), + ctypes.byref(jend2)) + + return (count, istart1.value, iend1.value, jstart1.value, + jend1.value, istart2.value, iend2.value, jstart2.value, jend2.value) + + + def overlap_contact_call(self): + pass diff --git a/tests/test_mosaic.py b/tests/test_mosaic.py new file mode 100644 index 0000000..059891d --- /dev/null +++ b/tests/test_mosaic.py @@ -0,0 +1,40 @@ +import pytest +import os +import numpy as np +from click.testing import CliRunner +from FMSgridtools.shared.gridobj import GridObj + +gridfiles = [f'grid.tile{x}.nc' for x in range(6)] +gridtiles = [f'tile{x}' for x in range(6)] +output = 'test_mosaic.nc' + +def test_write_function(): + mosaic = MosaicObj(ntiles=6, + mosaic_name='test_mosaic', + gridlocation='./', + gridfiles=np.asarray(gridfiles), + gridtiles=np.asarray(gridtiles), + contacts=np.full(6, "", dtype=str), + contact_index=np.full(6, "", dtype=str)) + mosaic.write_out_mosaic(output) + assert os.path.exists(output) == True + +def test_getntiles(): + mosaic = MosaicObj(mosaic_file=output) + ntiles = mosaic.get_ntiles() + assert ntiles == 6 + +def test_getgridfiles(): + mosaic2 = MosaicObj(mosaic_file=output) + assert mosaic2.gridfiles == gridfiles + os.remove(output) + +def test_solo_mosaic(): + #will attempt to utilize CliRunner() to test make_mosaic.py + pass + +def test_regional_mosaic(): + pass + + + From 8963a8f685f49e63231da2c4adc9cf64c4b0b854 Mon Sep 17 00:00:00 2001 From: hderry Date: Tue, 25 Feb 2025 17:01:11 -0500 Subject: [PATCH 07/24] fix typo for import in test file --- tests/test_mosaic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_mosaic.py b/tests/test_mosaic.py index 059891d..3a3675f 100644 --- a/tests/test_mosaic.py +++ b/tests/test_mosaic.py @@ -2,7 +2,7 @@ import os import numpy as np from click.testing import CliRunner -from FMSgridtools.shared.gridobj import GridObj +from FMSgridtools.shared.mosaicobj import MosaicObj gridfiles = [f'grid.tile{x}.nc' for x in range(6)] gridtiles = [f'tile{x}' for x in range(6)] From 18634328c88c29a7a3fb59fef20f0c10042a3a73 Mon Sep 17 00:00:00 2001 From: hderry Date: Wed, 26 Feb 2025 08:34:11 -0500 Subject: [PATCH 08/24] *created a separate object RegionalGridObj to create horizontal grid for regional data --- FMSgridtools/make_mosaic/make_mosaic.py | 14 +++----- FMSgridtools/make_mosaic/regionalgridobj.py | 40 +++++++++++++++++++++ tests/test_mosaic.py | 4 +-- 3 files changed, 46 insertions(+), 12 deletions(-) create mode 100644 FMSgridtools/make_mosaic/regionalgridobj.py diff --git a/FMSgridtools/make_mosaic/make_mosaic.py b/FMSgridtools/make_mosaic/make_mosaic.py index d9e4a98..e8fbf7f 100755 --- a/FMSgridtools/make_mosaic/make_mosaic.py +++ b/FMSgridtools/make_mosaic/make_mosaic.py @@ -4,6 +4,7 @@ import numpy as np from FMSgridtools.shared.gridobj import GridObj from FMSgridtools.shared.mosaicobj import MosaicObj +from FMSgridtools.make_mosaic.regionalgridobj import RegionalGridObj ATMOS_MOSAIC_HELP = "specify the atmosphere mosaic information \ This file contains list of tile files which specify \ @@ -254,16 +255,9 @@ def make_regional_mosaic( yt = global_m.grid_dict[f'tile{tile}'].y yarr = yt[round(2*j_min - 2):round(2*j_min - 2 + 2*ny+1), round(2*i_min - 2):round(2*i_min - 2 + 2*nx+1)] - tile = xr.DataArray(data=f'tile{tile}', attrs=dict(standard_name="grid_tile_spec")).astype('|S255') - x = xr.DataArray(data=xarr, dims=["nyp", "nxp"], attrs=dict( - standard_name="geographic_longitude", units="degree_east")) - y = xr.DataArray(data=yarr, dims=["nyp", "nxp"], attrs=dict( - standard_name="geographic_latitude", units="degree_north")) - - out_grid = xr.Dataset(data_vars={"tile": tile, "x": x, "y": y}) - - grid = GridObj(out_grid) - grid.write_out_grid(f"regional_grid.tile{tile}.nc") + regional_grid = RegionalGridObj(tile, xarr, + yarr) + regional_grid.write_out_regional_grid(f"regional_grid.tile{tile}.nc") print("\nCongratulations: You have successfully run make_regional_mosaic") diff --git a/FMSgridtools/make_mosaic/regionalgridobj.py b/FMSgridtools/make_mosaic/regionalgridobj.py new file mode 100644 index 0000000..ff574e9 --- /dev/null +++ b/FMSgridtools/make_mosaic/regionalgridobj.py @@ -0,0 +1,40 @@ +import numpy as np +import dataclasses +import xarray as xr +import numpy.typing as npt +from typing import List, Optional + +@dataclasses.dataclass +class RegionalGridObj: + tile: Optional[int] = None + x: Optional[npt.NDArray] = None + y: Optional[npt.NDArray] = None + + + def write_out_regional_grid(self, outfile: str): + + tile = xr.DataArray( + data=f'tile{self.tile}', + attrs=dict(standard_name="grid_tile_spec")).astype('|S255') + + x = xr.DataArray( + data=self.x, + dims=["nyp", "nxp"], + attrs=dict( + standard_name="geographic_longitude", units="degree_east")) + + y = xr.DataArray( + data=self.y, + dims=["nyp", "nxp"], + attrs=dict( + standard_name="geographic_latitude", units="degree_north")) + + ds = xr.Dataset( + data_vars={"tile": tile, + "x": x, + "y": y}) + + encoding = {'x': {'_FillValue': None}, + 'y': {'_FillValue': None}} + + ds.to_netcdf(outfile, encoding=encoding) diff --git a/tests/test_mosaic.py b/tests/test_mosaic.py index 3a3675f..71a5a9e 100644 --- a/tests/test_mosaic.py +++ b/tests/test_mosaic.py @@ -25,8 +25,8 @@ def test_getntiles(): assert ntiles == 6 def test_getgridfiles(): - mosaic2 = MosaicObj(mosaic_file=output) - assert mosaic2.gridfiles == gridfiles + mosaic = MosaicObj(mosaic_file=output) + assert mosaic.gridfiles == gridfiles os.remove(output) def test_solo_mosaic(): From 618fc9fead221744aec976f57a4ae03ce8faa50c Mon Sep 17 00:00:00 2001 From: hderry Date: Wed, 26 Feb 2025 15:06:03 -0500 Subject: [PATCH 09/24] cleaned up lines with collecting grid_data --- FMSgridtools/make_mosaic/make_mosaic.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/FMSgridtools/make_mosaic/make_mosaic.py b/FMSgridtools/make_mosaic/make_mosaic.py index e8fbf7f..218497e 100755 --- a/FMSgridtools/make_mosaic/make_mosaic.py +++ b/FMSgridtools/make_mosaic/make_mosaic.py @@ -170,10 +170,8 @@ def make_solo_mosaic( grid_data = {} for tile in gridtiles: grid_data[tile] = {} - xvals = mosaic.grid_dict[tile].x - grid_data[tile]['x'] = xvals - yvals = mosaic.grid_dict[tile].y - grid_data[tile]['y'] = yvals + grid_data[tile]['x'] = mosaic.grid_dict[tile].x + grid_data[tile]['y'] = mosaic.grid_dict[tile].y nxp = mosaic.grid_dict[tile].x.shape grid_data[tile]['nxp'] = nxp[1] nyp = mosaic.grid_dict[tile].y.shape @@ -213,7 +211,7 @@ def make_solo_mosaic( print(f"NOTE: There are {ncontact} contacts\n") if ncontact > 0: - mosaic = MosaicObj(mosaic_name, + mosaic = MosaicObj(mosaic_name=mosaic_name, gridlocation=dir, gridfiles=tilefiles, gridtiles=np.asarray(gridtiles), From 719c7a350825a908f188d67c4290dd396a19a0e5 Mon Sep 17 00:00:00 2001 From: hderry Date: Fri, 28 Feb 2025 15:26:31 -0500 Subject: [PATCH 10/24] make regional mosaic using MosaicObj --- FMSgridtools/make_mosaic/make_mosaic.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/FMSgridtools/make_mosaic/make_mosaic.py b/FMSgridtools/make_mosaic/make_mosaic.py index 218497e..ce2630c 100755 --- a/FMSgridtools/make_mosaic/make_mosaic.py +++ b/FMSgridtools/make_mosaic/make_mosaic.py @@ -253,9 +253,17 @@ def make_regional_mosaic( yt = global_m.grid_dict[f'tile{tile}'].y yarr = yt[round(2*j_min - 2):round(2*j_min - 2 + 2*ny+1), round(2*i_min - 2):round(2*i_min - 2 + 2*nx+1)] + outfile = f"regional_grid.tile{tile}.nc" + mosaic = "regional_mosaic.nc" + regional_grid = RegionalGridObj(tile, xarr, yarr) - regional_grid.write_out_regional_grid(f"regional_grid.tile{tile}.nc") + regional_grid.write_out_regional_grid(outfile) + + regional_mosaic = MosaicObj(mosaic_name="regoinal_mosaic", + gridfiles=np.array([outfile]), + gridtiles=np.array([f"tile{tile}"])) + regional_mosaic.write_out_regional_mosaic(mosaic) print("\nCongratulations: You have successfully run make_regional_mosaic") From 5306bc682104429bdf8c8ee9b524ef48c40fc850 Mon Sep 17 00:00:00 2001 From: hderry Date: Thu, 6 Mar 2025 12:22:58 -0500 Subject: [PATCH 11/24] *utilize cfrenctools library in wrapper mosaic_util.py *type hint functions in make_mosaic.py --- FMSgridtools/make_mosaic/make_mosaic.py | 6 ++--- .../pyfrenctools/make_mosaic/mosaic_util.py | 25 ++++++++++--------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/FMSgridtools/make_mosaic/make_mosaic.py b/FMSgridtools/make_mosaic/make_mosaic.py index ce2630c..04ed9c9 100755 --- a/FMSgridtools/make_mosaic/make_mosaic.py +++ b/FMSgridtools/make_mosaic/make_mosaic.py @@ -139,7 +139,7 @@ def make_solo_mosaic( mosaic_name, tile_file, periodx, - periody): + periody) -> None: """Generates mosaic information between tiles. The mosaic, information includes: list of tile files, list of contact region, specified by index, contact type.""" @@ -190,7 +190,7 @@ def make_solo_mosaic( grid_data[f'tile{n}']['y'], grid_data[f'tile{m}']['y'], periodx, periody) - count, istart1, iend1, jstart1, jend1, istart2, iend2, jstart2, jend2 = contact.align_contact(lib_file) + count, istart1, iend1, jstart1, jend1, istart2, iend2, jstart2, jend2 = contact.align_contact() if count > 0: contacts.append(f"{mosaic_name}:tile{n}::{mosaic_name}:tile{m}") @@ -221,7 +221,7 @@ def make_solo_mosaic( def make_regional_mosaic( global_mosaic, - regional_file): + regional_file) -> None: """ Generates a horizontal grid and solo mosaic for a regional output. The created grid and solo mosaic could be used to regrid regional output data onto regular lat-lon grid.""" diff --git a/FREnctools_lib/pyfrenctools/make_mosaic/mosaic_util.py b/FREnctools_lib/pyfrenctools/make_mosaic/mosaic_util.py index c75bf5b..712835e 100644 --- a/FREnctools_lib/pyfrenctools/make_mosaic/mosaic_util.py +++ b/FREnctools_lib/pyfrenctools/make_mosaic/mosaic_util.py @@ -1,27 +1,28 @@ import ctypes import dataclasses import numpy as np +import pyfrenctools import numpy.typing as npt from typing import Optional @dataclasses.dataclass class Contact: - tile1: Optional[int] = None - tile2: Optional[int] = None - nxp1: Optional[int] = None - nxp2: Optional[int] = None - nyp1: Optional[int] = None - nyp2: Optional[int] = None - x1: Optional[npt.NDArray] = None - x2: Optional[npt.NDArray] = None - y1: Optional[npt.NDArray] = None - y2: Optional[npt.NDArray] = None + tile1: int + tile2: int + nxp1: int + nxp2: int + nyp1: int + nyp2: int + x1: npt.NDArray + x2: npt.NDArray + y1: npt.NDArray + y2: npt.NDArray periodx: Optional[int] = None periody: Optional[int] = None - def align_contact(self, lib_file:str) -> int: + def align_contact(self) -> int: - clibrary = ctypes.CDLL(lib_file) + clibrary = pyfrenctools.cfrenctools.LIB().lib #acquire function signature find_align = clibrary.get_align_contact From 1692bc4f4b0a9341b015f05acebc401d265ea96a Mon Sep 17 00:00:00 2001 From: hderry Date: Thu, 24 Apr 2025 19:25:25 -0400 Subject: [PATCH 12/24] =?UTF-8?q?-=20Replaced=20dataclasses=20with=20stand?= =?UTF-8?q?ard=20python=20classes=20-=20Implemented=20subcommands=20with?= =?UTF-8?q?=20Click=20groups=E2=80=A6=20with=20each=20subcommand=20having?= =?UTF-8?q?=20its=20own=20module=20-=20Updated=20test=5Fmosaic.py=20to=20u?= =?UTF-8?q?tilize=20CliRunner=20for=20testing=20click=20applications?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FMSgridtools/make_mosaic/__init__.py | 1 + FMSgridtools/make_mosaic/make_mosaic.py | 314 +++++------------- FMSgridtools/make_mosaic/regional_mosaic.py | 50 +++ FMSgridtools/make_mosaic/regionalgridobj.py | 15 +- FMSgridtools/make_mosaic/solo_mosaic.py | 89 +++++ .../pyfrenctools/make_mosaic/mosaic_util.py | 53 ++- tests/test_mosaic.py | 29 +- 7 files changed, 293 insertions(+), 258 deletions(-) create mode 100644 FMSgridtools/make_mosaic/__init__.py create mode 100644 FMSgridtools/make_mosaic/regional_mosaic.py create mode 100644 FMSgridtools/make_mosaic/solo_mosaic.py diff --git a/FMSgridtools/make_mosaic/__init__.py b/FMSgridtools/make_mosaic/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/FMSgridtools/make_mosaic/__init__.py @@ -0,0 +1 @@ + diff --git a/FMSgridtools/make_mosaic/make_mosaic.py b/FMSgridtools/make_mosaic/make_mosaic.py index 04ed9c9..cd3529d 100755 --- a/FMSgridtools/make_mosaic/make_mosaic.py +++ b/FMSgridtools/make_mosaic/make_mosaic.py @@ -1,10 +1,7 @@ import click -import sys -import xarray as xr -import numpy as np -from FMSgridtools.shared.gridobj import GridObj -from FMSgridtools.shared.mosaicobj import MosaicObj -from FMSgridtools.make_mosaic.regionalgridobj import RegionalGridObj +import solo_mosaic +import regional_mosaic + ATMOS_MOSAIC_HELP = "specify the atmosphere mosaic information \ This file contains list of tile files which specify \ @@ -20,15 +17,61 @@ +mosaic_name = click.option('--mosaic_name', + default='mosaic', + help="mosaic name; The output file will be mosaic_name.nc.") + +sea_level = click.option('--sea_level', type=int, default=0) + +ocean_topog = click.option('--ocean_topog', + type=click.Path(exists=True)) + + + +@click.group() +def make_mosaic(): + '''MAKE MOSAIC CLI''' -@click.option('--num_tiles', +@make_mosaic.command() +@mosaic_name +@click.option("--num_tiles", type=int, + required=True, help="Number of tiles in the mosaic.") -@click.option('--dir', +@click.option('--dir_name', type=click.Path(exists=True, file_okay=False), help="the directory that contains all the tile grid files." ) +@click.option('--tile_file', '-f', + multiple=True, type=click.Path(exists=True, file_okay=True), + help=TILE_FILE_HELP) + +@click.option('--periodx', + default=0, + help = "Specify the period in x-direction of mosaic. \ + Default value is 0 (not periodic)") + +@click.option('--periody', default=0, + help="Specify the period in y-direction of mosaic. \ + Default value is 0 (not periodic)") + +def solo(num_tiles, + dir_name, + mosaic_name, + tile_file, + periodx, + periody): + + solo_mosaic.make(num_tiles, + dir_name, + mosaic_name, + tile_file, + periodx, + periody) + + +@make_mosaic.command() @click.option('--global_mosaic', type=click.Path(exists=True), help="global_mosaic Specify the mosaic file for the global grid.") @@ -37,9 +80,31 @@ type=click.Path(exists=True), help="regional_file Specify the regional model output file.") +def regional(global_mosaic, + regional_file): + + regional_mosaic.make(global_mosaic, + regional_file) + +@make_mosaic.command() +@mosaic_name +@sea_level +@ocean_topog @click.option('--input_mosaic', type=click.Path(exists=True)) +def quick(input_mosaic, + mosaic_name, + ocean_topog, + sea_level, + land_frac_file, + land_frac_field): + pass + +@make_mosaic.command() +@mosaic_name +@sea_level +@ocean_topog @click.option('--atmos_mosaic', type=click.Path(exists=True), help = ATMOS_MOSAIC_HELP) @@ -47,27 +112,6 @@ @click.option('--ocean_mosaic', type=click.Path(exists=True)) -@click.option('--ocean_topog', - type=click.Path(exists=True)) - -@click.option('--mosaic_name', default='mosaic', - help="mosaic name; The output file will be mosaic_name.nc.") - -@click.option('--tile_file', '-f', - multiple=True, type=click.Path(exists=True, file_okay=True), - help = TILE_FILE_HELP) - -@click.option('--periodx', - default=0, - help = "Specify the period in x-direction of mosaic. \ - Default value is 0 (not periodic)") - -@click.option('--periody', default=0, - help="Specify the period in y-direction of mosaic. \ - Default value is 0 (not periodic)") - -@click.option('--sea_level', type=int, default=0) - @click.option('--land_mosaic', type=click.Path(exists=True)) @click.option('--wave_mosaic', type=click.Path(exists=True)) @@ -78,212 +122,34 @@ @click.option('--check') -@click.option('--print_memory') - @click.option('--rotate_poly') -@click.argument('type') - -@click.command() -def main( - num_tiles, - dir, - mosaic_name, - tile_file, - periodx, - periody, - global_mosaic, - regional_file, - input_mosaic, - atmos_mosaic, - ocean_mosaic, - ocean_topog, - land_mosaic, - wave_mosaic, - interp_order, - area_ratio_thresh, - check, - sea_level, - print_memory, - rotate_poly, - type) -> None: - """main function""" - - if type == 'solo': - make_solo_mosaic( - num_tiles, dir, mosaic_name, - tile_file, periodx, - periody) - elif type == 'regional': - make_regional_mosaic( - global_mosaic, - regional_file) - else: - make_coupler_mosaic( - atmos_mosaic, +def coupler(atmos_mosaic, ocean_mosaic, ocean_topog, + mosaic_name, + sea_level, land_mosaic, wave_mosaic, interp_order, - sea_level, - mosaic_name, area_ratio_thresh, check, - print_memory, - rotate_poly) - -def make_solo_mosaic( - num_tiles, - dir, - mosaic_name, - tile_file, - periodx, - periody) -> None: - """Generates mosaic information between tiles. The mosaic, information includes: - list of tile files, list of contact region, - specified by index, contact type.""" - - contacts = [] - contact_index = [] - tilefiles = list(tile_file) - nfiles = len(tilefiles) - - if nfiles == 0: - if num_tiles == 1: - tilefiles.append("horizontal_grid.tile1.nc") - else: - for n in range(num_tiles): - tilefiles.append(f"horizontal_grid.tile{n}.nc") - else: - if nfiles != num_tiles: - sys.exit("Error: number tiles provided through --ntiles" - "does not match number of grid files passed through") - - gridtiles = [f'tile{i}' for i in range(1,nfiles+1)] - - - mosaic = MosaicObj(ntiles=num_tiles, gridfiles = np.asarray(tilefiles), - gridtiles = np.asarray(gridtiles)) - - mosaic.griddict() - grid_data = {} - for tile in gridtiles: - grid_data[tile] = {} - grid_data[tile]['x'] = mosaic.grid_dict[tile].x - grid_data[tile]['y'] = mosaic.grid_dict[tile].y - nxp = mosaic.grid_dict[tile].x.shape - grid_data[tile]['nxp'] = nxp[1] - nyp = mosaic.grid_dict[tile].y.shape - grid_data[tile]['nyp'] = nyp[0] - - ncontact = 0 - #FIND CONTACT REGIONS - for n in range(1,num_tiles): - for m in range(n,num_tiles+1): - contact = Contact(n, m, grid_data[f'tile{n}']['nxp'], - grid_data[f'tile{m}']['nxp'], - grid_data[f'tile{n}']['nyp'], - grid_data[f'tile{m}']['nyp'], - grid_data[f'tile{n}']['x'], - grid_data[f'tile{m}']['x'], - grid_data[f'tile{n}']['y'], - grid_data[f'tile{m}']['y'], - periodx, periody) - count, istart1, iend1, jstart1, jend1, istart2, iend2, jstart2, jend2 = contact.align_contact() - - if count > 0: - contacts.append(f"{mosaic_name}:tile{n}::{mosaic_name}:tile{m}") - ncontact+=1 - - tile1_istart = istart1 - tile1_iend = iend1 - tile1_jstart = jstart1 - tile1_jend = jend1 - tile2_istart = istart2 - tile2_iend = iend2 - tile2_jstart = jstart2 - tile2_jend = jend2 - - contact_index.append(f"{tile1_istart}:{tile1_iend},{tile1_jstart}:{tile1_jend}::{tile2_istart}:{tile2_iend},{tile2_jstart}:{tile2_jend}") - - print("\nCongratulations: You have successfully run solo mosaic") - print(f"NOTE: There are {ncontact} contacts\n") - - if ncontact > 0: - mosaic = MosaicObj(mosaic_name=mosaic_name, - gridlocation=dir, - gridfiles=tilefiles, - gridtiles=np.asarray(gridtiles), - contacts=np.asarray(contacts), - contact_index=np.asarray(contact_index)) - mosaic.write_out_mosaic(f'{mosaic_name}.nc') - -def make_regional_mosaic( - global_mosaic, - regional_file) -> None: - """ Generates a horizontal grid and solo mosaic for a regional output. - The created grid and solo mosaic could be used to regrid regional - output data onto regular lat-lon grid.""" - - #get tile number from regional file - tile = int(list(filter(str.isdigit, regional_file))[0]) - - ds = xr.open_dataset(regional_file) - nx = ds.sizes['grid_xt_sub01'] - ny = ds.sizes['grid_yt_sub01'] - - indx = ds.grid_xt_sub01.values - indy = ds.grid_yt_sub01.values - - i_min = np.min(indx) - i_max = np.max(indx) - j_min = np.min(indy) - j_max = np.max(indy) - - if i_max-i_min+1 != nx: - print("Error: make_regional_mosaic: i_max-i_min+1 != nx") - if j_max-j_min+1 != ny: - print("Error: make_regional_mosaic: j_max-j_min+1 != ny") - - global_m = MosaicObj(global_mosaic) - global_m.griddict() - xt = global_m.grid_dict[f'tile{tile}'].x - xarr = xt[round(2*j_min - 2):round(2*j_min - 2 + 2*ny+1), round(2*i_min - 2):round(2*i_min - 2 + 2*nx+1)] - yt = global_m.grid_dict[f'tile{tile}'].y - yarr = yt[round(2*j_min - 2):round(2*j_min - 2 + 2*ny+1), round(2*i_min - 2):round(2*i_min - 2 + 2*nx+1)] - - outfile = f"regional_grid.tile{tile}.nc" - mosaic = "regional_mosaic.nc" - - regional_grid = RegionalGridObj(tile, xarr, - yarr) - regional_grid.write_out_regional_grid(outfile) - - regional_mosaic = MosaicObj(mosaic_name="regoinal_mosaic", - gridfiles=np.array([outfile]), - gridtiles=np.array([f"tile{tile}"])) - regional_mosaic.write_out_regional_mosaic(mosaic) - - print("\nCongratulations: You have successfully run make_regional_mosaic") - -def make_coupler_mosaic( - atmos_mosaic, - ocean_mosaic, - ocean_topog, - land_mosaic, - wave_mosaic, - interp_order, - sea_level, - mosaic_name, - area_ratio_thresh, - check, - print_memory, - rotate_poly): + rotate_poly): + pass + #coupler.make(atmos_mosaic, + # ocean_mosaic, + # ocean_topog, + # mosaic_name, + # sea_level, + # land_mosaic, + # wave_mosaic, + # interp_order, + # area_ratio_thresh, + # check + # rotate_poly) - pass if __name__ == '__main__': - main() + make_mosaic() diff --git a/FMSgridtools/make_mosaic/regional_mosaic.py b/FMSgridtools/make_mosaic/regional_mosaic.py new file mode 100644 index 0000000..d69dfad --- /dev/null +++ b/FMSgridtools/make_mosaic/regional_mosaic.py @@ -0,0 +1,50 @@ +import xarray as xr +import numpy as np +from FMSgridtools.shared.mosaicobj import MosaicObj +from .regionalgridobj import RegionalGridObj + +def make(global_mosaic, + regional_file): -> None + """ Generates a horizontal grid and solo mosaic for a regional output. + The created grid and solo mosaic could be used to regrid regional + output data onto regular lat-lon grid.""" + + #get tile number from regional file + tile = int(list(filter(str.isdigit, regional_file))[0]) + + ds = xr.open_dataset(regional_file) + nx = ds.sizes['grid_xt_sub01'] + ny = ds.sizes['grid_yt_sub01'] + + indx = ds.grid_xt_sub01.values + indy = ds.grid_yt_sub01.values + + i_min = np.min(indx) + i_max = np.max(indx) + j_min = np.min(indy) + j_max = np.max(indy) + + if i_max-i_min+1 != nx: + print("Error: make_regional_mosaic: i_max-i_min+1 != nx") + if j_max-j_min+1 != ny: + print("Error: make_regional_mosaic: j_max-j_min+1 != ny") + + global_m = MosaicObj(global_mosaic) + global_m.griddict() + xt = global_m.grid_dict[f'tile{tile}'].x + xarr = xt[round(2*j_min - 2):round(2*j_min - 2 + 2*ny+1), round(2*i_min - 2):round(2*i_min - 2 + 2*nx+1)] + yt = global_m.grid_dict[f'tile{tile}'].y + yarr = yt[round(2*j_min - 2):round(2*j_min - 2 + 2*ny+1), round(2*i_min - 2):round(2*i_min - 2 + 2*nx+1)] + + outfile = f"regional_grid.tile{tile}.nc" + mosaic = "regional_mosaic.nc" + regional_grid = RegionalGridObj(tile, xarr, + yarr) + regional_grid.write_out_regional_grid(outfile) + regional_mosaic = MosaicObj(mosaic_name="regoinal_mosaic", + gridfiles=np.array([outfile]), + gridtiles=np.array([f"tile{tile}"])) + regional_mosaic.write_out_regional_mosaic(mosaic) + + print("\nCongratulations: You have successfully run regional mosaic") + diff --git a/FMSgridtools/make_mosaic/regionalgridobj.py b/FMSgridtools/make_mosaic/regionalgridobj.py index ff574e9..c91dce7 100644 --- a/FMSgridtools/make_mosaic/regionalgridobj.py +++ b/FMSgridtools/make_mosaic/regionalgridobj.py @@ -1,14 +1,15 @@ import numpy as np -import dataclasses import xarray as xr import numpy.typing as npt -from typing import List, Optional -@dataclasses.dataclass -class RegionalGridObj: - tile: Optional[int] = None - x: Optional[npt.NDArray] = None - y: Optional[npt.NDArray] = None +class RegionalGridObj(self, + tile: int, + x: npt.NDArray, + y: npt.NDArray): + + self.tile: tile + self.x = x + self.y = y def write_out_regional_grid(self, outfile: str): diff --git a/FMSgridtools/make_mosaic/solo_mosaic.py b/FMSgridtools/make_mosaic/solo_mosaic.py new file mode 100644 index 0000000..5edab50 --- /dev/null +++ b/FMSgridtools/make_mosaic/solo_mosaic.py @@ -0,0 +1,89 @@ +import sys +import numpy as np +from FMSgridtools.shared.gridobj import GridObj +from FMSgridtools.shared.mosaicobj import MosaicObj +from .regionalgridobj import RegionalGridObj +import pyfrenctools + +def make(num_tiles, + dir_name, + mosaic_name, + tile_file, + periodx, + periody) -> None: + """Generates mosaic information between tiles. The mosaic information includes: + list of tile files, list of contact region, + specified by index, contact type.""" + + contacts = [] + contact_index = [] + tilefiles = list(tile_file) + nfiles = len(tilefiles) + + if nfiles == 0: + if num_tiles == 1: + tilefiles.append("horizontal_grid.tile1.nc") + else: + for n in range(num_tiles): + tilefiles.append(f"horizontal_grid.tile{n}.nc") + else: + if nfiles != num_tiles: + sys.exit("Error: number tiles provided through --ntiles" + "does not match number of grid files passed through") + + gridtiles = [f'tile{i}' for i in range(1,nfiles+1)] + + + mosaic = MosaicObj(ntiles=num_tiles, gridfiles = np.asarray(tilefiles), + gridtiles = np.asarray(gridtiles)) + mosaic.griddict() + + grid_data = {} + for tile in gridtiles: + grid_data[tile] = {} + grid_data[tile]['x'] = mosaic.grid_dict[tile].x + grid_data[tile]['y'] = mosaic.grid_dict[tile].y + grid_data[tile]['nxp'] = mosaic.grid_dict[tile].nxp + grid_data[tile]['nyp'] = mosaic.grid_dict[tile].nyp + + ncontact = 0 + #FIND CONTACT REGIONS + for n in range(1,num_tiles): + for m in range(n,num_tiles+1): + contact = Contact(n, m, grid_data[f'tile{n}']['nxp'], + grid_data[f'tile{m}']['nxp'], + grid_data[f'tile{n}']['nyp'], + grid_data[f'tile{m}']['nyp'], + grid_data[f'tile{n}']['x'], + grid_data[f'tile{m}']['x'], + grid_data[f'tile{n}']['y'], + grid_data[f'tile{m}']['y'], + periodx, periody) + count, istart1, iend1, jstart1, jend1, istart2, iend2, jstart2, jend2 = contact.align_contact() + + if count > 0: + contacts.append(f"{mosaic_name}:tile{n}::{mosaic_name}:tile{m}") + ncontact+=1 + + tile1_istart = istart1 + tile1_iend = iend1 + tile1_jstart = jstart1 + tile1_jend = jend1 + tile2_istart = istart2 + tile2_iend = iend2 + tile2_jstart = jstart2 + tile2_jend = jend2 + + contact_index.append(f"{tile1_istart}:{tile1_iend},{tile1_jstart}:{tile1_jend}::{tile2_istart}:{tile2_iend},{tile2_jstart}:{tile2_jend}") + + print("\nCongratulations: You have successfully run solo mosaic") + print(f"NOTE: There are {ncontact} contacts\n") + + if ncontact > 0: + mosaic = MosaicObj(mosaic_name=mosaic_name, + gridlocation=dir_name, + gridfiles=tilefiles, + gridtiles=np.asarray(gridtiles), + contacts=np.asarray(contacts), + contact_index=np.asarray(contact_index)) + mosaic.write_out_mosaic(f'{mosaic_name}.nc') diff --git a/FREnctools_lib/pyfrenctools/make_mosaic/mosaic_util.py b/FREnctools_lib/pyfrenctools/make_mosaic/mosaic_util.py index 712835e..74c72c4 100644 --- a/FREnctools_lib/pyfrenctools/make_mosaic/mosaic_util.py +++ b/FREnctools_lib/pyfrenctools/make_mosaic/mosaic_util.py @@ -1,29 +1,45 @@ -import ctypes import dataclasses +import ctypes +from ctypes import create_string_buffer +from typing import Optional import numpy as np -import pyfrenctools import numpy.typing as npt -from typing import Optional +from cfrenctools import LIB +import pathlib +from numpy.ctypeslib import ndpointer -@dataclasses.dataclass class Contact: - tile1: int - tile2: int - nxp1: int - nxp2: int - nyp1: int - nyp2: int - x1: npt.NDArray - x2: npt.NDArray - y1: npt.NDArray - y2: npt.NDArray - periodx: Optional[int] = None - periody: Optional[int] = None + def __init__(self, + tile1: int, + tile2: int, + nxp1: int, + nxp2: int, + nyp1: int, + nyp2: int, + x1: npt.NDArray, + x2: npt.NDArray, + y1: npt.NDArray, + y2: npt.NDArray, + periodx: int = None, + periody: int = None): + + self.tile1 = tile1 + self.tile2 = tile2 + self.nxp1 = nxp1 + self.nxp2 = nxp2 + self.nyp1 = nyp1 + self.nyp2 = nyp2 + self.x1 = x1 + self.x2 = x2 + self.y1 = y1 + self.y2 = y2 + self.periodx = periodx + self.periody = periody def align_contact(self) -> int: - clibrary = pyfrenctools.cfrenctools.LIB().lib - + #clibrary = ctypes.CDLL(lib_file) + clibrary = LIB('/home/Halle.Derry/savegit/libcontact.so').lib #acquire function signature find_align = clibrary.get_align_contact @@ -78,3 +94,4 @@ def align_contact(self) -> int: def overlap_contact_call(self): pass + diff --git a/tests/test_mosaic.py b/tests/test_mosaic.py index 71a5a9e..44ec6fc 100644 --- a/tests/test_mosaic.py +++ b/tests/test_mosaic.py @@ -3,9 +3,12 @@ import numpy as np from click.testing import CliRunner from FMSgridtools.shared.mosaicobj import MosaicObj +from FMSgridtools.make_mosaic.make_mosaic import make_mosaic gridfiles = [f'grid.tile{x}.nc' for x in range(6)] + gridtiles = [f'tile{x}' for x in range(6)] + output = 'test_mosaic.nc' def test_write_function(): @@ -17,7 +20,7 @@ def test_write_function(): contacts=np.full(6, "", dtype=str), contact_index=np.full(6, "", dtype=str)) mosaic.write_out_mosaic(output) - assert os.path.exists(output) == True + assert os.path.exists(output) is True def test_getntiles(): mosaic = MosaicObj(mosaic_file=output) @@ -25,16 +28,24 @@ def test_getntiles(): assert ntiles == 6 def test_getgridfiles(): - mosaic = MosaicObj(mosaic_file=output) - assert mosaic.gridfiles == gridfiles + mosaic2 = MosaicObj(mosaic_file=output) + assert mosaic2.gridfiles == gridfiles os.remove(output) def test_solo_mosaic(): - #will attempt to utilize CliRunner() to test make_mosaic.py - pass - -def test_regional_mosaic(): - pass - + runner = CliRunner() + result = runner.invoke(make_mosaic, ['solo', + '--num_tiles', '2', + '--tile_file', 'C192_grid.tile1.nc', + '--tile_file', 'C192_grid.tile2.nc']) + assert result.exit_code == 0 + assert 'NOTE: There are 1 contacts' in result.stdout +def test_regional_mosaic(): + runner = CliRunner() + result = runner.invoke(make_mosaic, ['regional', + '--global_mosaic', 'C48_mosaic.nc', + '--regional_file', 'rregion_input_file.tile3.nc']) + assert result.exit_code == 0 + assert 'Congratulations: You have successfully run regional mosaic' in result.stdout From f4a718cf59749f183e08d5aadd76106c522cfef4 Mon Sep 17 00:00:00 2001 From: hderry Date: Fri, 25 Apr 2025 11:28:35 -0400 Subject: [PATCH 13/24] fix import statment and use of clib in mosaic_utils --- FMSgridtools/make_mosaic/solo_mosaic.py | 1 - FREnctools_lib/pyfrenctools/make_mosaic/mosaic_util.py | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/FMSgridtools/make_mosaic/solo_mosaic.py b/FMSgridtools/make_mosaic/solo_mosaic.py index 5edab50..90ada65 100644 --- a/FMSgridtools/make_mosaic/solo_mosaic.py +++ b/FMSgridtools/make_mosaic/solo_mosaic.py @@ -2,7 +2,6 @@ import numpy as np from FMSgridtools.shared.gridobj import GridObj from FMSgridtools.shared.mosaicobj import MosaicObj -from .regionalgridobj import RegionalGridObj import pyfrenctools def make(num_tiles, diff --git a/FREnctools_lib/pyfrenctools/make_mosaic/mosaic_util.py b/FREnctools_lib/pyfrenctools/make_mosaic/mosaic_util.py index 74c72c4..a6f9b58 100644 --- a/FREnctools_lib/pyfrenctools/make_mosaic/mosaic_util.py +++ b/FREnctools_lib/pyfrenctools/make_mosaic/mosaic_util.py @@ -1,4 +1,3 @@ -import dataclasses import ctypes from ctypes import create_string_buffer from typing import Optional @@ -38,8 +37,7 @@ def __init__(self, def align_contact(self) -> int: - #clibrary = ctypes.CDLL(lib_file) - clibrary = LIB('/home/Halle.Derry/savegit/libcontact.so').lib + clibrary = pyfrenctools.cfrenctools.LIB().lib #acquire function signature find_align = clibrary.get_align_contact From 2944d46f2ed732af4cefd2bb62626545bf7c2bac Mon Sep 17 00:00:00 2001 From: hderry Date: Fri, 2 May 2025 11:54:54 -0400 Subject: [PATCH 14/24] solo_mosaic -remove conversion of lists to np.arrays -> change to be refected on mosaicobj side as well. -use tile names directly in solo_mosaic --- FMSgridtools/make_mosaic/solo_mosaic.py | 30 +++++++++---------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/FMSgridtools/make_mosaic/solo_mosaic.py b/FMSgridtools/make_mosaic/solo_mosaic.py index 90ada65..570b772 100644 --- a/FMSgridtools/make_mosaic/solo_mosaic.py +++ b/FMSgridtools/make_mosaic/solo_mosaic.py @@ -33,17 +33,17 @@ def make(num_tiles, gridtiles = [f'tile{i}' for i in range(1,nfiles+1)] - mosaic = MosaicObj(ntiles=num_tiles, gridfiles = np.asarray(tilefiles), - gridtiles = np.asarray(gridtiles)) - mosaic.griddict() + read = MosaicObj(ntiles=num_tiles, gridfiles = tilefiles, + gridtiles = gridtiles) + read.griddict() grid_data = {} for tile in gridtiles: grid_data[tile] = {} - grid_data[tile]['x'] = mosaic.grid_dict[tile].x - grid_data[tile]['y'] = mosaic.grid_dict[tile].y - grid_data[tile]['nxp'] = mosaic.grid_dict[tile].nxp - grid_data[tile]['nyp'] = mosaic.grid_dict[tile].nyp + grid_data[tile]['x'] = read.grid_dict[tile].x + grid_data[tile]['y'] = read.grid_dict[tile].y + grid_data[tile]['nxp'] = read.grid_dict[tile].nxp + grid_data[tile]['nyp'] = read.grid_dict[tile].nyp ncontact = 0 #FIND CONTACT REGIONS @@ -64,16 +64,8 @@ def make(num_tiles, contacts.append(f"{mosaic_name}:tile{n}::{mosaic_name}:tile{m}") ncontact+=1 - tile1_istart = istart1 - tile1_iend = iend1 - tile1_jstart = jstart1 - tile1_jend = jend1 - tile2_istart = istart2 - tile2_iend = iend2 - tile2_jstart = jstart2 - tile2_jend = jend2 - contact_index.append(f"{tile1_istart}:{tile1_iend},{tile1_jstart}:{tile1_jend}::{tile2_istart}:{tile2_iend},{tile2_jstart}:{tile2_jend}") + contact_index.append(f"{istart1}:{iend1},{jstart1}:{jend1}::{istart2}:{iend2},{jstart2}:{jend2}") print("\nCongratulations: You have successfully run solo mosaic") print(f"NOTE: There are {ncontact} contacts\n") @@ -82,7 +74,7 @@ def make(num_tiles, mosaic = MosaicObj(mosaic_name=mosaic_name, gridlocation=dir_name, gridfiles=tilefiles, - gridtiles=np.asarray(gridtiles), - contacts=np.asarray(contacts), - contact_index=np.asarray(contact_index)) + gridtiles=gridtiles, + contacts=contacts, + contact_index=contact_index) mosaic.write_out_mosaic(f'{mosaic_name}.nc') From 6f3c40acb6e8bfcf4775e7473d4f984aed4ec975 Mon Sep 17 00:00:00 2001 From: hderry Date: Fri, 2 May 2025 12:45:33 -0400 Subject: [PATCH 15/24] mosaic.griddict will now return self.grid_dict making new instance in solo_mosaic > grid = MosaicObj(args).griddict() --- FMSgridtools/make_mosaic/solo_mosaic.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/FMSgridtools/make_mosaic/solo_mosaic.py b/FMSgridtools/make_mosaic/solo_mosaic.py index 570b772..0bb553d 100644 --- a/FMSgridtools/make_mosaic/solo_mosaic.py +++ b/FMSgridtools/make_mosaic/solo_mosaic.py @@ -33,17 +33,16 @@ def make(num_tiles, gridtiles = [f'tile{i}' for i in range(1,nfiles+1)] - read = MosaicObj(ntiles=num_tiles, gridfiles = tilefiles, - gridtiles = gridtiles) - read.griddict() - + grid = MosaicObj(ntiles=num_tiles, gridfiles = tilefiles, + gridtiles = gridtiles).griddict() + grid_data = {} for tile in gridtiles: grid_data[tile] = {} - grid_data[tile]['x'] = read.grid_dict[tile].x - grid_data[tile]['y'] = read.grid_dict[tile].y - grid_data[tile]['nxp'] = read.grid_dict[tile].nxp - grid_data[tile]['nyp'] = read.grid_dict[tile].nyp + grid_data[tile]['x'] = grid[tile].x + grid_data[tile]['y'] = grid[tile].y + grid_data[tile]['nxp'] = grid[tile].nxp + grid_data[tile]['nyp'] = grid[tile].nyp ncontact = 0 #FIND CONTACT REGIONS From 30f273f10b29f2e5a4090937ba4de5f98cb4f52b Mon Sep 17 00:00:00 2001 From: hderry Date: Fri, 2 May 2025 12:51:56 -0400 Subject: [PATCH 16/24] match mosaic objects --- FMSgridtools/make_mosaic/solo_mosaic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/FMSgridtools/make_mosaic/solo_mosaic.py b/FMSgridtools/make_mosaic/solo_mosaic.py index 0bb553d..755735d 100644 --- a/FMSgridtools/make_mosaic/solo_mosaic.py +++ b/FMSgridtools/make_mosaic/solo_mosaic.py @@ -70,10 +70,10 @@ def make(num_tiles, print(f"NOTE: There are {ncontact} contacts\n") if ncontact > 0: - mosaic = MosaicObj(mosaic_name=mosaic_name, + grid = MosaicObj(mosaic_name=mosaic_name, gridlocation=dir_name, gridfiles=tilefiles, gridtiles=gridtiles, contacts=contacts, contact_index=contact_index) - mosaic.write_out_mosaic(f'{mosaic_name}.nc') + grid.write_out_mosaic(f'{mosaic_name}.nc') From b3bf2ee95fbbe82a3b8b5cc01d3753749f088ce7 Mon Sep 17 00:00:00 2001 From: hderry Date: Fri, 2 May 2025 12:53:05 -0400 Subject: [PATCH 17/24] revert last changes --- FMSgridtools/make_mosaic/solo_mosaic.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/FMSgridtools/make_mosaic/solo_mosaic.py b/FMSgridtools/make_mosaic/solo_mosaic.py index 755735d..0bb553d 100644 --- a/FMSgridtools/make_mosaic/solo_mosaic.py +++ b/FMSgridtools/make_mosaic/solo_mosaic.py @@ -70,10 +70,10 @@ def make(num_tiles, print(f"NOTE: There are {ncontact} contacts\n") if ncontact > 0: - grid = MosaicObj(mosaic_name=mosaic_name, + mosaic = MosaicObj(mosaic_name=mosaic_name, gridlocation=dir_name, gridfiles=tilefiles, gridtiles=gridtiles, contacts=contacts, contact_index=contact_index) - grid.write_out_mosaic(f'{mosaic_name}.nc') + mosaic.write_out_mosaic(f'{mosaic_name}.nc') From 34daf794d0864fd05180fd90117b9084a26ffc9c Mon Sep 17 00:00:00 2001 From: hderry Date: Mon, 12 May 2025 10:39:22 -0400 Subject: [PATCH 18/24] Moved write function from regionalgridobj to regional_mosaic.py --- FMSgridtools/make_mosaic/regional_mosaic.py | 52 ++++++++++++++++----- FMSgridtools/make_mosaic/regionalgridobj.py | 41 ---------------- 2 files changed, 40 insertions(+), 53 deletions(-) delete mode 100644 FMSgridtools/make_mosaic/regionalgridobj.py diff --git a/FMSgridtools/make_mosaic/regional_mosaic.py b/FMSgridtools/make_mosaic/regional_mosaic.py index d69dfad..beae86e 100644 --- a/FMSgridtools/make_mosaic/regional_mosaic.py +++ b/FMSgridtools/make_mosaic/regional_mosaic.py @@ -1,10 +1,9 @@ import xarray as xr import numpy as np from FMSgridtools.shared.mosaicobj import MosaicObj -from .regionalgridobj import RegionalGridObj def make(global_mosaic, - regional_file): -> None + regional_file): """ Generates a horizontal grid and solo mosaic for a regional output. The created grid and solo mosaic could be used to regrid regional output data onto regular lat-lon grid.""" @@ -29,22 +28,51 @@ def make(global_mosaic, if j_max-j_min+1 != ny: print("Error: make_regional_mosaic: j_max-j_min+1 != ny") - global_m = MosaicObj(global_mosaic) - global_m.griddict() - xt = global_m.grid_dict[f'tile{tile}'].x - xarr = xt[round(2*j_min - 2):round(2*j_min - 2 + 2*ny+1), round(2*i_min - 2):round(2*i_min - 2 + 2*nx+1)] - yt = global_m.grid_dict[f'tile{tile}'].y - yarr = yt[round(2*j_min - 2):round(2*j_min - 2 + 2*ny+1), round(2*i_min - 2):round(2*i_min - 2 + 2*nx+1)] + grid = MosaicObj(global_mosaic).griddict() + x = grid[f'tile{tile}'].x + xarr = x[round(2*j_min - 2):round(2*j_min - 2 + 2*ny+1), round(2*i_min - 2):round(2*i_min - 2 + 2*nx+1)] + y = grid[f'tile{tile}'].y + yarr = y[round(2*j_min - 2):round(2*j_min - 2 + 2*ny+1), round(2*i_min - 2):round(2*i_min - 2 + 2*nx+1)] outfile = f"regional_grid.tile{tile}.nc" + write_out_regional_grid(tile, xarr, + yarr, outfile) + mosaic = "regional_mosaic.nc" - regional_grid = RegionalGridObj(tile, xarr, - yarr) - regional_grid.write_out_regional_grid(outfile) - regional_mosaic = MosaicObj(mosaic_name="regoinal_mosaic", + regional_mosaic = MosaicObj(mosaic_name="regional_mosaic", gridfiles=np.array([outfile]), gridtiles=np.array([f"tile{tile}"])) regional_mosaic.write_out_regional_mosaic(mosaic) print("\nCongratulations: You have successfully run regional mosaic") + +def write_out_regional_grid(tile, xarr, yarr, outfile): + + tile = xr.DataArray( + data=f'tile{tile}', + attrs=dict(standard_name="grid_tile_spec")).astype('|S255') + + x = xr.DataArray( + data=xarr, + dims=["nyp", "nxp"], + attrs=dict( + standard_name="geographic_longitude", units="degree_east")) + + y = xr.DataArray( + data=yarr, + dims=["nyp", "nxp"], + attrs=dict( + standard_name="geographic_latitude", units="degree_north")) + + ds = xr.Dataset( + data_vars={"tile": tile, + "x": x, + "y": y}) + + encoding = {'x': {'_FillValue': None}, + 'y': {'_FillValue': None}} + + ds.to_netcdf(outfile, encoding=encoding) + + diff --git a/FMSgridtools/make_mosaic/regionalgridobj.py b/FMSgridtools/make_mosaic/regionalgridobj.py deleted file mode 100644 index c91dce7..0000000 --- a/FMSgridtools/make_mosaic/regionalgridobj.py +++ /dev/null @@ -1,41 +0,0 @@ -import numpy as np -import xarray as xr -import numpy.typing as npt - -class RegionalGridObj(self, - tile: int, - x: npt.NDArray, - y: npt.NDArray): - - self.tile: tile - self.x = x - self.y = y - - - def write_out_regional_grid(self, outfile: str): - - tile = xr.DataArray( - data=f'tile{self.tile}', - attrs=dict(standard_name="grid_tile_spec")).astype('|S255') - - x = xr.DataArray( - data=self.x, - dims=["nyp", "nxp"], - attrs=dict( - standard_name="geographic_longitude", units="degree_east")) - - y = xr.DataArray( - data=self.y, - dims=["nyp", "nxp"], - attrs=dict( - standard_name="geographic_latitude", units="degree_north")) - - ds = xr.Dataset( - data_vars={"tile": tile, - "x": x, - "y": y}) - - encoding = {'x': {'_FillValue': None}, - 'y': {'_FillValue': None}} - - ds.to_netcdf(outfile, encoding=encoding) From 800a711f4ba6ef1c28c01035064c47043b448cef Mon Sep 17 00:00:00 2001 From: hderry Date: Tue, 13 May 2025 12:30:56 -0400 Subject: [PATCH 19/24] update test_mosaic.py to generate tile(s) so testing can be done freely without depending on importing input files --- tests/test_mosaic.py | 93 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 80 insertions(+), 13 deletions(-) diff --git a/tests/test_mosaic.py b/tests/test_mosaic.py index 44ec6fc..d0a8a30 100644 --- a/tests/test_mosaic.py +++ b/tests/test_mosaic.py @@ -1,15 +1,76 @@ -import pytest import os +import pytest +import random import numpy as np +import xarray as xr from click.testing import CliRunner -from FMSgridtools.shared.mosaicobj import MosaicObj +from FMSgridtools.shared.mosaicobj import MosaicObj from FMSgridtools.make_mosaic.make_mosaic import make_mosaic -gridfiles = [f'grid.tile{x}.nc' for x in range(6)] +DEFAULT_GRID_SIZE = 48 +DEFAULT_TILE_NUMBER = 1 +gridfiles = [f'grid.tile{x}.nc' for x in range(6)] gridtiles = [f'tile{x}' for x in range(6)] - +tile_number = DEFAULT_TILE_NUMBER output = 'test_mosaic.nc' +tilefile = 'tile1.nc' + + +def test_create_tile(): + xstart, xend = 0, 360 + ystart, yend = -90, 90 + + x = np.arange(xstart, xend, dtype=float) + y = np.arange(ystart, yend, dtype=float) + x, y = np.meshgrid(x, y) + + xarr = xr.DataArray( + data = x, + dims = ["nyp", "nxp"]).astype(np.float64) + + yarr = xr.DataArray( + data = y, + dims = ["nyp", "nxp"]).astype(np.float64) + + tile = xr.Dataset( + data_vars={"x": xarr, + "y": yarr + }).astype({"x": np.float64, "y": np.float64}) + tile.to_netcdf(tilefile) + +def test_create_regional_input(): + grid_size = DEFAULT_GRID_SIZE + + nx = 1 + random.randint(1,100) % grid_size + ny = 1 + random.randint(1,100) % grid_size + + nx_start = 1 + random.randint(1,100) % (grid_size - nx + 1) + ny_start = 1 + random.randint(1,100) % (grid_size - nx + 1) + + xt = [] + yt= [] + + for i in range(1,nx+1): + xt.append(nx_start+i) + + for i in range(1,ny+1): + yt.append(ny_start+i) + + xt_data = xr.DataArray( + data = xt, + dims = ["grid_xt_sub01"]).astype(np.float64) + + yt_data = xr.DataArray( + data = yt, + dims = ["grid_yt_sub01"]).astype(np.float64) + + xr.Dataset( + data_vars={ + "grid_xt_sub01": xt_data, + "grid_yt_sub01": yt_data}).to_netcdf( + f"regional_input_file.tile{tile_number}.nc") + def test_write_function(): mosaic = MosaicObj(ntiles=6, @@ -34,18 +95,24 @@ def test_getgridfiles(): def test_solo_mosaic(): runner = CliRunner() - result = runner.invoke(make_mosaic, ['solo', - '--num_tiles', '2', - '--tile_file', 'C192_grid.tile1.nc', + result = runner.invoke(make_mosaic, ['solo', + '--num_tiles', '2', + '--tile_file', 'C192_grid.tile1.nc', '--tile_file', 'C192_grid.tile2.nc']) - assert result.exit_code == 0 + assert result.exit_code == 0 assert 'NOTE: There are 1 contacts' in result.stdout - + os.remove('mosaic.nc') + os.remove(tilefile) def test_regional_mosaic(): runner = CliRunner() - result = runner.invoke(make_mosaic, ['regional', - '--global_mosaic', 'C48_mosaic.nc', - '--regional_file', 'rregion_input_file.tile3.nc']) - assert result.exit_code == 0 + result = runner.invoke(make_mosaic, ['regional', + '--global_mosaic', + 'C48_mosaic.nc', + '--regional_file', + 'regional_input_file.tile1.nc']) + assert result.exit_code == 0 assert 'Congratulations: You have successfully run regional mosaic' in result.stdout + os.remove('regional_mosaic.nc') + os.remove(f'regional_grid.tile{tile_number}.nc') + os.remove(f'regional_input_file.tile{tile_number}.nc') From 00c1fc4da2df5a30de1f059ba7ec777f4fc2c50a Mon Sep 17 00:00:00 2001 From: hderry Date: Tue, 13 May 2025 13:13:26 -0400 Subject: [PATCH 20/24] error handling added to regional_mosaic.py --- FMSgridtools/make_mosaic/regional_mosaic.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/FMSgridtools/make_mosaic/regional_mosaic.py b/FMSgridtools/make_mosaic/regional_mosaic.py index beae86e..b788efb 100644 --- a/FMSgridtools/make_mosaic/regional_mosaic.py +++ b/FMSgridtools/make_mosaic/regional_mosaic.py @@ -9,7 +9,10 @@ def make(global_mosaic, output data onto regular lat-lon grid.""" #get tile number from regional file - tile = int(list(filter(str.isdigit, regional_file))[0]) + try: + tile = int(list(filter(str.isdigit, regional_file))[0]) + except IndexError: + sys.exit("make_regional_mosaic: tile number not found in the regional_file name") ds = xr.open_dataset(regional_file) nx = ds.sizes['grid_xt_sub01'] @@ -29,9 +32,11 @@ def make(global_mosaic, print("Error: make_regional_mosaic: j_max-j_min+1 != ny") grid = MosaicObj(global_mosaic).griddict() - x = grid[f'tile{tile}'].x + try: + x,y = grid[f'tile{tile}'].x, grid[f'tile{tile}'].y + except KeyError: + sys.exit("make_regional_mosaic: gridtile with matching tile number not found") xarr = x[round(2*j_min - 2):round(2*j_min - 2 + 2*ny+1), round(2*i_min - 2):round(2*i_min - 2 + 2*nx+1)] - y = grid[f'tile{tile}'].y yarr = y[round(2*j_min - 2):round(2*j_min - 2 + 2*ny+1), round(2*i_min - 2):round(2*i_min - 2 + 2*nx+1)] outfile = f"regional_grid.tile{tile}.nc" From 6a4f7f8a2d0c40c9b85114cda4b68d9ac54c71e3 Mon Sep 17 00:00:00 2001 From: hderry Date: Tue, 13 May 2025 13:33:57 -0400 Subject: [PATCH 21/24] just adding an import line --- FMSgridtools/make_mosaic/regional_mosaic.py | 1 + 1 file changed, 1 insertion(+) diff --git a/FMSgridtools/make_mosaic/regional_mosaic.py b/FMSgridtools/make_mosaic/regional_mosaic.py index b788efb..b326b98 100644 --- a/FMSgridtools/make_mosaic/regional_mosaic.py +++ b/FMSgridtools/make_mosaic/regional_mosaic.py @@ -1,3 +1,4 @@ +import sys import xarray as xr import numpy as np from FMSgridtools.shared.mosaicobj import MosaicObj From 560ccc7fb3ae476fabc1a049202f64df56d5ef47 Mon Sep 17 00:00:00 2001 From: hderry Date: Tue, 13 May 2025 15:24:23 -0400 Subject: [PATCH 22/24] * set datatype when creating array with np.arange instead of using astype * change assert command --- tests/test_mosaic.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/tests/test_mosaic.py b/tests/test_mosaic.py index d0a8a30..2038235 100644 --- a/tests/test_mosaic.py +++ b/tests/test_mosaic.py @@ -21,23 +21,21 @@ def test_create_tile(): xstart, xend = 0, 360 ystart, yend = -90, 90 - x = np.arange(xstart, xend, dtype=float) - y = np.arange(ystart, yend, dtype=float) + x = np.arange(xstart, xend, dtype=np.float64) + y = np.arange(ystart, yend, dtype=np.float64) x, y = np.meshgrid(x, y) xarr = xr.DataArray( data = x, - dims = ["nyp", "nxp"]).astype(np.float64) + dims = ["nyp", "nxp"]) yarr = xr.DataArray( data = y, - dims = ["nyp", "nxp"]).astype(np.float64) + dims = ["nyp", "nxp"]) tile = xr.Dataset( data_vars={"x": xarr, - "y": yarr - }).astype({"x": np.float64, "y": np.float64}) - tile.to_netcdf(tilefile) + "y": yarr}).to_netcdf(tilefile) def test_create_regional_input(): grid_size = DEFAULT_GRID_SIZE @@ -81,7 +79,7 @@ def test_write_function(): contacts=np.full(6, "", dtype=str), contact_index=np.full(6, "", dtype=str)) mosaic.write_out_mosaic(output) - assert os.path.exists(output) is True + assert os.path.exists(output) def test_getntiles(): mosaic = MosaicObj(mosaic_file=output) From f8d41833d55deeb2b23a114bad08e136ef35f8cb Mon Sep 17 00:00:00 2001 From: hderry Date: Thu, 5 Jun 2025 10:14:19 -0400 Subject: [PATCH 23/24] minor changes to fix test_mosaic.py --- tests/test_mosaic.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tests/test_mosaic.py b/tests/test_mosaic.py index 2038235..4fd1ded 100644 --- a/tests/test_mosaic.py +++ b/tests/test_mosaic.py @@ -82,13 +82,12 @@ def test_write_function(): assert os.path.exists(output) def test_getntiles(): - mosaic = MosaicObj(mosaic_file=output) - ntiles = mosaic.get_ntiles() - assert ntiles == 6 + mosaic = MosaicObj(mosaic_file=output).read() + assert mosaic.ntiles == 6 def test_getgridfiles(): - mosaic2 = MosaicObj(mosaic_file=output) - assert mosaic2.gridfiles == gridfiles + mosaic = MosaicObj(mosaic_file=output).read() + assert set(mosaic.gridfiles) == (gridfiles) os.remove(output) def test_solo_mosaic(): From 9d27e9badf8db1e21672add80deebb69c4a0f158 Mon Sep 17 00:00:00 2001 From: hderry Date: Thu, 5 Jun 2025 11:29:31 -0400 Subject: [PATCH 24/24] create set of list --- tests/test_mosaic.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_mosaic.py b/tests/test_mosaic.py index 4fd1ded..1742bfa 100644 --- a/tests/test_mosaic.py +++ b/tests/test_mosaic.py @@ -87,7 +87,7 @@ def test_getntiles(): def test_getgridfiles(): mosaic = MosaicObj(mosaic_file=output).read() - assert set(mosaic.gridfiles) == (gridfiles) + assert set(mosaic.gridfiles) == set(gridfiles) os.remove(output) def test_solo_mosaic():