diff --git a/.github/workflows/prebuild.yaml b/.github/workflows/prebuild.yaml index 80e34608..3f8085fb 100644 --- a/.github/workflows/prebuild.yaml +++ b/.github/workflows/prebuild.yaml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.10"] + python-version: ["3.8","3.9","3.10","3.11","3.12"] steps: - uses: actions/checkout@v4 @@ -35,8 +35,7 @@ jobs: run: | export PYTHONPATH=$(pwd)/scripts:$(pwd)/scripts/parse_tools cd test_prebuild - python3 test_metadata_parser.py - python3 test_mkstatic.py + pytest - name: ccpp-prebuild blocked data tests run: | cd test_prebuild/test_blocked_data diff --git a/.github/workflows/python.yaml b/.github/workflows/python.yaml deleted file mode 100644 index bf1e7c36..00000000 --- a/.github/workflows/python.yaml +++ /dev/null @@ -1,49 +0,0 @@ -name: Python package - -on: - workflow_dispatch: - pull_request: - branches: [feature/capgen, main] - -jobs: - build: - runs-on: ubuntu-latest - strategy: - matrix: - python-version: ['3.8', '3.9', '3.10', '3.11'] - - steps: - - uses: actions/checkout@v3 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install pytest - - name: Test with pytest - run: | - export PYTHONPATH=$(pwd)/scripts:$(pwd)/scripts/parse_tools - pytest -v - - doctest: - runs-on: ubuntu-latest - strategy: - matrix: - python-version: ['3.8', '3.9', '3.10', '3.11'] - - steps: - - uses: actions/checkout@v3 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install pytest - - name: Doctest - run: | - export PYTHONPATH=$(pwd)/scripts:$(pwd)/scripts/parse_tools - pytest -v scripts/ --doctest-modules diff --git a/pytest.ini b/pytest.ini deleted file mode 100644 index d08180f1..00000000 --- a/pytest.ini +++ /dev/null @@ -1,2 +0,0 @@ -[pytest] -addopts = -ra --ignore=scripts/metadata2html.py --ignore-glob=test/**/test_reports.py \ No newline at end of file diff --git a/scripts/common.py b/scripts/common.py index a1bc0efd..c33f78fe 100755 --- a/scripts/common.py +++ b/scripts/common.py @@ -30,6 +30,10 @@ CCPP_BLOCK_SIZES = 'ccpp_block_sizes' CCPP_THREAD_NUMBER = 'ccpp_thread_number' +CCPP_CHUNK_EXTENT = 'ccpp_chunk_extent' +CCPP_HORIZONTAL_LOOP_BEGIN = 'horizontal_loop_begin' +CCPP_HORIZONTAL_LOOP_END = 'horizontal_loop_end' + CCPP_HORIZONTAL_LOOP_EXTENT = 'horizontal_loop_extent' CCPP_HORIZONTAL_DIMENSION = 'horizontal_dimension' diff --git a/scripts/mkstatic.py b/scripts/mkstatic.py index 576abdf8..57067d4b 100755 --- a/scripts/mkstatic.py +++ b/scripts/mkstatic.py @@ -16,6 +16,7 @@ from common import CCPP_STAGES from common import CCPP_T_INSTANCE_VARIABLE, CCPP_ERROR_CODE_VARIABLE, CCPP_ERROR_MSG_VARIABLE, CCPP_LOOP_COUNTER, CCPP_LOOP_EXTENT from common import CCPP_BLOCK_NUMBER, CCPP_BLOCK_COUNT, CCPP_BLOCK_SIZES, CCPP_THREAD_NUMBER, CCPP_INTERNAL_VARIABLES +from common import CCPP_HORIZONTAL_LOOP_BEGIN, CCPP_HORIZONTAL_LOOP_END, CCPP_CHUNK_EXTENT from common import CCPP_CONSTANT_ONE, CCPP_HORIZONTAL_DIMENSION, CCPP_HORIZONTAL_LOOP_EXTENT, CCPP_NUM_INSTANCES from common import FORTRAN_CONDITIONAL_REGEX_WORDS, FORTRAN_CONDITIONAL_REGEX from common import CCPP_TYPE, STANDARD_VARIABLE_TYPES, STANDARD_CHARACTER_TYPE @@ -100,6 +101,47 @@ def extract_parents_and_indices_from_local_name(local_name): indices = [ x.strip() for x in indices ] return (parent, indices) +def extract_dimensions_from_local_name(local_name): + """Extract the dimensions from a local_name. + Throw away any parent information.""" + # First, find delimiter '%' between parent(s) and child '%' + parent_delimiter_index = -1 + if '%' in local_name: + i = len(local_name)-1 + opened = 0 + while i >= 0: + if local_name[i] == ')': + opened += 1 + elif local_name[i] == '(': + opened -= 1 + elif local_name[i] == '%' and opened == 0: + parent_delimiter_index = i + break + i -= 1 + if '(' in local_name[parent_delimiter_index+1:]: + dim_string_start = local_name[parent_delimiter_index+1:].find('(') + dim_string_end = local_name[parent_delimiter_index+1:].rfind(')') + dim_string = local_name[parent_delimiter_index+1:][dim_string_start:dim_string_end+1] + # Now that we have a dim_string, find all dimensions in this string; + # ignore outermost opening and closing parentheses. + opened = 0 + dim = '' + i = 1 + dimensions = [] + while i <= len(dim_string)-1: + if dim_string[i] == ',' and opened == 0: + dimensions.append(dim) + dim = '' + elif i == len(dim_string)-1 and dim: + dimensions.append(dim) + else: + dim += dim_string[i] + i += 1 + else: + dimensions = [] + dim_string = '' + return (dimensions, dim_string) + def create_argument_list_wrapped(arguments): """Create a wrapped argument list, remove trailing ',' """ argument_list = '' @@ -1049,10 +1091,28 @@ def write(self, metadata_request, metadata_define, arguments, debug): args = '' length = 0 - # First identify all dimensions needed to handle the arguments - # and add them to the list of required variables for the cap + # First, add a few mandatory variables to the list of required + # variables. This is mostly for handling horizontal dimensions + # correctly for the different CCPP phases and for cases when + # blocked data structures or chunked arrays are used. additional_variables_required = [] - # + if CCPP_HORIZONTAL_LOOP_EXTENT in metadata_define.keys(): + for add_var in [ CCPP_CONSTANT_ONE, CCPP_HORIZONTAL_LOOP_EXTENT]: + if not add_var in local_vars.keys() \ + and not add_var in additional_variables_required + arguments[scheme_name][subroutine_name]: + logging.debug("Adding variable {} for handling blocked data structures".format(add_var)) + additional_variables_required.append(add_var) + elif ccpp_stage == 'run' and \ + CCPP_HORIZONTAL_LOOP_BEGIN in metadata_define.keys() and \ + CCPP_HORIZONTAL_LOOP_END in metadata_define.keys() and \ + CCPP_CHUNK_EXTENT in metadata_define.keys(): + for add_var in [ CCPP_HORIZONTAL_LOOP_BEGIN, CCPP_HORIZONTAL_LOOP_END, CCPP_CHUNK_EXTENT]: + if not add_var in local_vars.keys() \ + and not add_var in additional_variables_required + arguments[scheme_name][subroutine_name]: + logging.debug("Adding variable {} for handling chunked data arrays".format(add_var)) + additional_variables_required.append(add_var) + # Next, identify all dimensions needed to handle the arguments + # and add them to the list of required variables for the cap for var_standard_name in arguments[scheme_name][subroutine_name]: if not var_standard_name in metadata_define.keys(): raise Exception('Variable {standard_name} not defined in host model metadata'.format( @@ -1076,18 +1136,11 @@ def write(self, metadata_request, metadata_define, arguments, debug): # If blocked data structures need to be converted, add necessary variables if ccpp_stage in ['init', 'timestep_init', 'timestep_finalize', 'finalize'] and CCPP_INTERNAL_VARIABLES[CCPP_BLOCK_NUMBER] in var.local_name: - if not CCPP_BLOCK_COUNT in local_vars.keys() \ - and not CCPP_BLOCK_COUNT in additional_variables_required + arguments[scheme_name][subroutine_name]: - logging.debug("Adding variable {} for handling blocked data structures".format(CCPP_BLOCK_COUNT)) - additional_variables_required.append(CCPP_BLOCK_COUNT) - if not CCPP_HORIZONTAL_LOOP_EXTENT in local_vars.keys() \ - and not CCPP_HORIZONTAL_LOOP_EXTENT in additional_variables_required + arguments[scheme_name][subroutine_name]: - logging.debug("Adding variable {} for handling blocked data structures".format(CCPP_HORIZONTAL_LOOP_EXTENT)) - additional_variables_required.append(CCPP_HORIZONTAL_LOOP_EXTENT) - if not CCPP_HORIZONTAL_DIMENSION in local_vars.keys() \ - and not CCPP_HORIZONTAL_DIMENSION in additional_variables_required + arguments[scheme_name][subroutine_name]: - logging.debug("Adding variable {} for handling blocked data structures".format(CCPP_HORIZONTAL_DIMENSION)) - additional_variables_required.append(CCPP_HORIZONTAL_DIMENSION) + for add_var in [ CCPP_BLOCK_COUNT, CCPP_HORIZONTAL_DIMENSION]: + if not add_var in local_vars.keys() \ + and not add_var in additional_variables_required + arguments[scheme_name][subroutine_name]: + logging.debug("Adding variable {} for handling blocked data structures".format(add_var)) + additional_variables_required.append(add_var) # If the variable is only active/used under certain conditions, add necessary variables # also record the conditional for later use in unit conversions / blocked data conversions. @@ -1263,6 +1316,158 @@ def write(self, metadata_request, metadata_define, arguments, debug): if container == var.container: break + # We need some information about the host model variable + (dimensions_target_name, dim_string_target_name) = extract_dimensions_from_local_name(var.target) + + # Derive correct horizontal loop extent for this variable for the rest of this function + if var.rank: + array_size = [] + dim_substrings = [] + for dim in var.dimensions: + # DH* TODO + # By default, don't pass explicit dimensions, only ':'. This is because + # we have not solved the problem of passing inactive arrays correctly + # (something that needs to be done before we can use chunked arrays in + # models like the ufs-weather-model). See also note further down where + # this variable gets set to True and then where it gets used. + use_explicit_dimension = False + # *DH + # This is not supported/implemented: tmpvar would have one dimension less + # than the original array, and the metadata requesting the variable would + # not pass the initial test that host model variables and scheme variables + # have the same rank. + if dim == CCPP_BLOCK_NUMBER: + raise Exception("{} cannot be part of the dimensions of variable {}".format( + CCPP_BLOCK_NUMBER, var_standard_name)) + else: + # Handle dimensions like "A:B", "A:3", "-1:Z" + if ':' in dim: + dims = [ x.lower() for x in dim.split(':')] + try: + dim0 = int(dims[0]) + dim0 = dims[0] + except ValueError: + if not dims[0].lower() in metadata_define.keys(): + raise Exception('Dimension {}, required by variable {}, not defined in host model metadata'.format( + dims[0].lower(), var_standard_name)) + dim0 = metadata_define[dims[0].lower()][0].local_name + try: + dim1 = int(dims[1]) + dim1 = dims[1] + except ValueError: + # Use correct horizontal variables in run phase + if ccpp_stage == 'run' and dims[1].lower() == CCPP_HORIZONTAL_LOOP_EXTENT: + # Provide backward compatibility with blocked data structures + # and bypass the unresolved problems with inactive data + + # For this, we need to check if the host model variable + # is a contiguous array (it's horizontal dimension is + # CCPP_HORIZONTAL_DIMENSION) or part of a blocked data + # structure (it's horizontal dimension is CCPP_HORIZONTAL_LOOP_EXTENT) + for dim in metadata_define[var_standard_name][0].dimensions: + if ':' in dim: + host_var_dims = [x.lower() for x in dim.split(':')] + # Single dimensions are indices and should not be recorded as a dimension! + else: + raise Exception("THIS SHOULD NOT HAPPEN WITH CAPGEN'S METADATA PARSER") + if CCPP_HORIZONTAL_DIMENSION in host_var_dims: + host_var_is_contiguous = True + elif CCPP_HORIZONTAL_LOOP_EXTENT in host_var_dims: + host_var_is_contiguous = False + if CCPP_HORIZONTAL_LOOP_BEGIN in metadata_define.keys() and host_var_is_contiguous: + dim0 = metadata_define[CCPP_HORIZONTAL_LOOP_BEGIN][0].local_name + dim1 = metadata_define[CCPP_HORIZONTAL_LOOP_END][0].local_name + use_explicit_dimension = True + else: + dim0 = metadata_define[CCPP_CONSTANT_ONE][0].local_name + dim1 = metadata_define[CCPP_HORIZONTAL_LOOP_EXTENT][0].local_name + # Remove this variable so that we can catch errors + # if it doesn't get set even though it should + del host_var_is_contiguous + else: + if not dims[1].lower() in metadata_define.keys(): + raise Exception('Dimension {}, required by variable {}, not defined in host model metadata'.format( + dims[1].lower(), var_standard_name)) + dim1 = metadata_define[dims[1].lower()][0].local_name + # Single dimensions are indices and should not be recorded as a dimension! + else: + raise Exception("THIS SHOULD NOT HAPPEN WITH CAPGEN'S METADATA PARSER") + + # DH* TODO REMOVE THIS ENTIRE BLOCK in a future PR to feature/capgen + # This block should not be needed, the metadata parser should take care + # of flagging invalid dimensions for the host model or the physics. + # TODO: create a test suite to make sure these things are caught + # by the metadata parser - do this on the feature/capgen branch! + if ccpp_stage == 'run': + # This should not happen when parsing metadata with capgen's metadata parser, remove? + if dims[1] == CCPP_HORIZONTAL_LOOP_EXTENT and not dim0: + raise Exception(f"Invalid metadata for scheme {scheme_name}: " + \ + f"horizontal dimension for {var_standard_name} is {var.dimensions}") + # This should not happen when parsing metadata with capgen's metadata parser, remove? + elif CCPP_HORIZONTAL_LOOP_BEGIN in dims or CCPP_HORIZONTAL_LOOP_END in dims or \ + CCPP_HORIZONTAL_DIMENSION in dims: + raise Exception(f"Invalid metadata for scheme {scheme_name}: " + \ + f"horizontal dimension for {var_standard_name} is {var.dimensions}") + else: + # This should not happen when parsing metadata with capgen's metadata parser, remove? + if dims[1] == CCPP_HORIZONTAL_DIMENSION and not dim0: + raise Exception(f"Invalid metadata for scheme {scheme_name}: " + \ + f"horizontal dimension for {var_standard_name} is {var.dimensions}") + # This should not happen when parsing metadata with capgen's metadata parser, remove? + if CCPP_HORIZONTAL_LOOP_BEGIN in dims or CCPP_HORIZONTAL_LOOP_END in dims or \ + CCPP_LOOP_EXTENT in dims: + raise Exception(f"Invalid metadata for scheme {scheme_name}: " + \ + f"horizontal dimension for {var_standard_name} is {var.dimensions}") + # *DH + + # DH* TODO: WE CANNOT ACTIVATE USING EXPLICIT HORIZONTAL DIMENSIONS + # UNTIL WE HAVE SOLVED THE PROBLEM WITH INACTIVE (NON-ALLOCATED) + # ARRAYS. THIS MUST BE ADDRESSED BEFORE WE SWITCH TO CONTIGUOUS + # ARRAYS FOR MODELS LIKE THE UFS-WEATHER-MODEL! + if use_explicit_dimension: + if dim0 == dim1: + array_size.append('1') + dim_substrings.append(f'{dim1}') + else: + array_size.append(f'({dim1}-{dim0}+1)') + dim_substrings.append(f'{dim0}:{dim1}') + else: + if dim0 == dim1: + array_size.append('1') + # DH* TODO - is this correct? + dim_substrings.append(f':') + else: + array_size.append(f'({dim1}-{dim0}+1)') + dim_substrings.append(f':') + + # Now we need to compare dim_substrings with a possible dim_string_target_name and merge them + if dimensions_target_name: + if len(dimensions_target_name) < len(dim_substrings): + raise Exception("THIS SHOULD NOT HAPPEN") + dim_counter = 0 + dim_string = '(' + for dim in dimensions_target_name: + if ':' in dim: + dim_string += dim_substrings[dim_counter] + ',' + dim_counter += 1 + else: + dim_string += dim + ',' + dim_string = dim_string.rstrip(',') + ')' + # Consistency check to make sure all dimensions from metadata are 'used' + if dim_counter < len(dim_substrings): + raise Exception(f"Mismatch of derived dimensions from metadata {dim_substrings} " + \ + f"vs target local name {dimensions_target_name} for {var_standard_name} and " + \ + f"scheme {scheme_name} / phase {ccpp_stage}") + else: + dim_string = '({})'.format(','.join(dim_substrings)) + var_size_expected = '({})'.format('*'.join(array_size)) + else: + if dimensions_target_name: + dim_string = dim_string_target_name + else: + dim_string = '' + var_size_expected = 1 + # To assist debugging efforts, check if arrays have the correct size (ignore scalars for now) assign_test = '' if debug: @@ -1274,56 +1479,14 @@ def write(self, metadata_request, metadata_define, arguments, debug): # deals with non-uniform block sizes etc. pass elif var.rank: - array_size = [] - for dim in var.dimensions: - # This is not supported/implemented: tmpvar would have one dimension less - # than the original array, and the metadata requesting the variable would - # not pass the initial test that host model variables and scheme variables - # have the same rank. - if dim == CCPP_BLOCK_NUMBER: - raise Exception("{} cannot be part of the dimensions of variable {}".format( - CCPP_BLOCK_NUMBER, var_standard_name)) - else: - # Handle dimensions like "A:B", "A:3", "-1:Z" - if ':' in dim: - dims = [ x.lower() for x in dim.split(':')] - try: - dim0 = int(dims[0]) - dim0 = dims[0] - except ValueError: - if not dims[0].lower() in metadata_define.keys(): - raise Exception('Dimension {}, required by variable {}, not defined in host model metadata'.format( - dims[0].lower(), var_standard_name)) - dim0 = metadata_define[dims[0].lower()][0].local_name - try: - dim1 = int(dims[1]) - dim1 = dims[1] - except ValueError: - if not dims[1].lower() in metadata_define.keys(): - raise Exception('Dimension {}, required by variable {}, not defined in host model metadata'.format( - dims[1].lower(), var_standard_name)) - dim1 = metadata_define[dims[1].lower()][0].local_name - # Single dimensions - else: - dim0 = 1 - try: - dim1 = int(dim) - dim1 = dim - except ValueError: - if not dim.lower() in metadata_define.keys(): - raise Exception('Dimension {}, required by variable {}, not defined in host model metadata'.format( - dim.lower(), var_standard_name)) - dim1 = metadata_define[dim.lower()][0].local_name - array_size.append('({last}-{first}+1)'.format(last=dim1, first=dim0)) - var_size_expected = '({})'.format('*'.join(array_size)) assign_test = ''' ! Check if variable {var_name} is associated/allocated and has the correct size - if (size({var_name})/={var_size_expected}) then - write({ccpp_errmsg}, '(2(a,i8))') 'Detected size mismatch for variable {var_name} in group {group_name} before {subroutine_name}, expected ', & - {var_size_expected}, ' but got ', size({var_name}) + if (size({var_name}{dim_string})/={var_size_expected}) then + write({ccpp_errmsg}, '(2(a,i8))') 'Detected size mismatch for variable {var_name}{dim_string} in group {group_name} before {subroutine_name}, expected ', & + {var_size_expected}, ' but got ', size({var_name}{dim_string}) ierr = 1 return end if -'''.format(var_name=local_vars[var_standard_name]['name'], var_size_expected=var_size_expected, +'''.format(var_name=local_vars[var_standard_name]['name'].replace(dim_string_target_name, ''), dim_string=dim_string, var_size_expected=var_size_expected, ccpp_errmsg=CCPP_INTERNAL_VARIABLES[CCPP_ERROR_MSG_VARIABLE], group_name = self.name, subroutine_name=subroutine_name) # end if debug @@ -1388,6 +1551,10 @@ def write(self, metadata_request, metadata_define, arguments, debug): var_defs_manual.append('integer :: ib, nb') # Define actions before. Always copy data in, independent of intent. + # We intentionally omit the dim string for the assignment on the right hand side, + # since it worked without until now, since coding this up together with chunked array + # logic is tricky, and since all this logic will go away after the models transitioned + # to chunked arrays. actions_in = ''' ! Allocate local variable to copy blocked data {var} into a contiguous array allocate({tmpvar}({dims})) ib = 1 @@ -1492,21 +1659,20 @@ def write(self, metadata_request, metadata_define, arguments, debug): tmpvar.local_name = 'tmpvar{0}'.format(tmpvar_cnt) tmpvars[local_vars[var_standard_name]['name']] = tmpvar if tmpvar.rank: - # Add allocate statement if the variable has a rank > 0 - # According to https://fortran-lang.discourse.group/t/allocated-array-bounds-with-mold-and-source-on-expressions/3032, - # allocating with 'source=...' sets the correct lower and upper bounds for the newly created array - actions_in += ' allocate({t}, source={v})\n'.format(t=tmpvar.local_name, - v=tmpvar.target) + # Add allocate statement if the variable has a rank > 0 using the dimstring derived above + actions_in += f' allocate({tmpvar.local_name}{dim_string})' if var.actions['in']: # Add unit conversion before entering the subroutine - actions_in += ' {t} = {c}\n'.format(t=tmpvar.local_name, - c=var.actions['in'].format(var=tmpvar.target, - kind=kind_string)) + actions_in += ' {t} = {c}{d}\n'.format(t=tmpvar.local_name, + c=var.actions['in'].format(var=tmpvar.target.replace(dim_string_target_name, ''), + kind=kind_string), + d=dim_string) if var.actions['out']: # Add unit conversion after returning from the subroutine - actions_out += ' {v} = {c}\n'.format(v=tmpvar.target, - c=var.actions['out'].format(var=tmpvar.local_name, - kind=kind_string)) + actions_out += ' {v}{d} = {c}\n'.format(v=tmpvar.target.replace(dim_string_target_name, ''), + d=dim_string, + c=var.actions['out'].format(var=tmpvar.local_name, + kind=kind_string)) if tmpvar.rank: # Add deallocate statement if the variable has a rank > 0 actions_out += ' deallocate({t})\n'.format(t=tmpvar.local_name) @@ -1527,7 +1693,8 @@ def write(self, metadata_request, metadata_define, arguments, debug): actions_out=actions_out.rstrip('\n')) # Add to argument list - arg = '{local_name}={var_name},'.format(local_name=var.local_name, var_name=tmpvar.local_name) + arg = '{local_name}={var_name}{dim_string},'.format(local_name=var.local_name, + var_name=tmpvar.local_name.replace(dim_string_target_name, ''), dim_string=dim_string) # Ordinary variables, no blocked data or unit conversions elif var_standard_name in arguments[scheme_name][subroutine_name]: @@ -1542,8 +1709,8 @@ def write(self, metadata_request, metadata_define, arguments, debug): actions_in=actions_in.rstrip('\n')) # Add to argument list - arg = '{local_name}={var_name},'.format(local_name=var.local_name, - var_name=local_vars[var_standard_name]['name']) + arg = '{local_name}={var_name}{dim_string},'.format(local_name=var.local_name, + var_name=local_vars[var_standard_name]['name'].replace(dim_string_target_name, ''), dim_string=dim_string) else: arg = '' args += arg @@ -1611,6 +1778,14 @@ def write(self, metadata_request, metadata_define, arguments, debug): if subcycle_body: body += subcycle_body_prefix + subcycle_body + subcycle_body_suffix + #For the init stage, for the case when the suite doesn't have any schemes with init phases, + #we still need to add the host-supplied ccpp_t variable to the init group caps so that it is + #available for setting the initialized flag for the particular instance being called. Otherwise, + #the initialized_set_block for the init phase tries to reference the unavailable ccpp_t variable. + if (ccpp_stage == 'init' and not self.parents[ccpp_stage]): + ccpp_var.intent = 'in' + self.parents[ccpp_stage].update({ccpp_var.standard_name:ccpp_var}) + # Get list of arguments, module use statement and variable definitions for this subroutine (=stage for the group) (self.arguments[ccpp_stage], sub_module_use, sub_var_defs) = create_arguments_module_use_var_defs( self.parents[ccpp_stage], metadata_define, tmpvars.values()) diff --git a/scripts/parse_tools/parse_checkers.py b/scripts/parse_tools/parse_checkers.py index f32df45d..1a0d1565 100755 --- a/scripts/parse_tools/parse_checkers.py +++ b/scripts/parse_tools/parse_checkers.py @@ -14,10 +14,11 @@ _UNITLESS_REGEX = "1" _NON_LEADING_ZERO_NUM = "[1-9]\d*" +_CHAR_WITH_UNDERSCORE = "([a-zA-Z]+_[a-zA-Z]+)+" _NEGATIVE_NON_LEADING_ZERO_NUM = f"[-]{_NON_LEADING_ZERO_NUM}" _UNIT_EXPONENT = f"({_NEGATIVE_NON_LEADING_ZERO_NUM}|{_NON_LEADING_ZERO_NUM})" _UNIT_REGEX = f"[a-zA-Z]+{_UNIT_EXPONENT}?" -_UNITS_REGEX = f"^({_UNIT_REGEX}(\s{_UNIT_REGEX})*|{_UNITLESS_REGEX})$" +_UNITS_REGEX = f"^({_CHAR_WITH_UNDERSCORE}|{_UNIT_REGEX}(\s{_UNIT_REGEX})*|{_UNITLESS_REGEX})$" _UNITS_RE = re.compile(_UNITS_REGEX) _MAX_MOLAR_MASS = 10000.0 diff --git a/src/ccpp_types.F90 b/src/ccpp_types.F90 index bebb53cb..dafa7c80 100644 --- a/src/ccpp_types.F90 +++ b/src/ccpp_types.F90 @@ -35,8 +35,10 @@ module ccpp_types integer, parameter :: CCPP_DEFAULT_LOOP_CNT = -999 integer, parameter :: CCPP_DEFAULT_LOOP_MAX = -999 - !> @var The default values for block and thread numbers indicating invalid data - integer, parameter :: CCPP_DEFAULT_BLOCK_AND_THREAD_NUMBER = -999 + !> @var The default values for block, chunk and thread numbers indicating invalid data + integer, parameter :: CCPP_DEFAULT_BLOCK_NUMBER = -999 + integer, parameter :: CCPP_DEFAULT_CHUNK_NUMBER = -999 + integer, parameter :: CCPP_DEFAULT_THREAD_NUMBER = -999 !! \section arg_table_ccpp_t !! \htmlinclude ccpp_t.html @@ -56,8 +58,9 @@ module ccpp_types character(len=512) :: errmsg = '' integer :: loop_cnt = CCPP_DEFAULT_LOOP_CNT integer :: loop_max = CCPP_DEFAULT_LOOP_MAX - integer :: blk_no = CCPP_DEFAULT_BLOCK_AND_THREAD_NUMBER - integer :: thrd_no = CCPP_DEFAULT_BLOCK_AND_THREAD_NUMBER + integer :: blk_no = CCPP_DEFAULT_BLOCK_NUMBER + integer :: chunk_no = CCPP_DEFAULT_CHUNK_NUMBER + integer :: thrd_no = CCPP_DEFAULT_THREAD_NUMBER integer :: ccpp_instance = 1 contains @@ -74,8 +77,9 @@ function ccpp_t_initialized(ccpp_d) result(initialized) class(ccpp_t) :: ccpp_d logical :: initialized ! - initialized = (ccpp_d%blk_no /= CCPP_DEFAULT_BLOCK_AND_THREAD_NUMBER .and. & - ccpp_d%thrd_no /= CCPP_DEFAULT_BLOCK_AND_THREAD_NUMBER) + initialized = ccpp_d%thrd_no /= CCPP_DEFAULT_THREAD_NUMBER .or. & + ccpp_d%blk_no /= CCPP_DEFAULT_BLOCK_NUMBER .or. & + ccpp_d%chunk_no /= CCPP_DEFAULT_CHUNK_NUMBER end function ccpp_t_initialized end module ccpp_types diff --git a/src/ccpp_types.meta b/src/ccpp_types.meta index 6ad84522..41a01f52 100644 --- a/src/ccpp_types.meta +++ b/src/ccpp_types.meta @@ -38,6 +38,12 @@ units = index dimensions = () type = integer +[chunk_no] + standard_name = ccpp_chunk_number + long_name = number of chunk for using array chunks in CCPP + units = index + dimensions = () + type = integer [thrd_no] standard_name = ccpp_thread_number long_name = number of thread for threading in CCPP diff --git a/test_prebuild/test_chunked_data/CMakeLists.txt b/test_prebuild/test_chunked_data/CMakeLists.txt new file mode 100644 index 00000000..0d055768 --- /dev/null +++ b/test_prebuild/test_chunked_data/CMakeLists.txt @@ -0,0 +1,90 @@ +#------------------------------------------------------------------------------ +cmake_minimum_required(VERSION 3.0) + +project(ccpp_chunked_data + VERSION 1.0.0 + LANGUAGES Fortran) + +#------------------------------------------------------------------------------ +# Request a static build +option(BUILD_SHARED_LIBS "Build a shared library" OFF) + +#------------------------------------------------------------------------------ +# Set the sources: physics type definitions +set(TYPEDEFS $ENV{CCPP_TYPEDEFS}) +if(TYPEDEFS) + message(STATUS "Got CCPP TYPEDEFS from environment variable: ${TYPEDEFS}") +else(TYPEDEFS) + include(${CMAKE_CURRENT_BINARY_DIR}/CCPP_TYPEDEFS.cmake) + message(STATUS "Got CCPP TYPEDEFS from cmakefile include file: ${TYPEDEFS}") +endif(TYPEDEFS) + +# Generate list of Fortran modules from the CCPP type +# definitions that need need to be installed +foreach(typedef_module ${TYPEDEFS}) + list(APPEND MODULES_F90 ${CMAKE_CURRENT_BINARY_DIR}/${typedef_module}) +endforeach() + +#------------------------------------------------------------------------------ +# Set the sources: physics schemes +set(SCHEMES $ENV{CCPP_SCHEMES}) +if(SCHEMES) + message(STATUS "Got CCPP SCHEMES from environment variable: ${SCHEMES}") +else(SCHEMES) + include(${CMAKE_CURRENT_BINARY_DIR}/CCPP_SCHEMES.cmake) + message(STATUS "Got CCPP SCHEMES from cmakefile include file: ${SCHEMES}") +endif(SCHEMES) + +# Set the sources: physics scheme caps +set(CAPS $ENV{CCPP_CAPS}) +if(CAPS) + message(STATUS "Got CCPP CAPS from environment variable: ${CAPS}") +else(CAPS) + include(${CMAKE_CURRENT_BINARY_DIR}/CCPP_CAPS.cmake) + message(STATUS "Got CCPP CAPS from cmakefile include file: ${CAPS}") +endif(CAPS) + +# Set the sources: physics scheme caps +set(API $ENV{CCPP_API}) +if(API) + message(STATUS "Got CCPP API from environment variable: ${API}") +else(API) + include(${CMAKE_CURRENT_BINARY_DIR}/CCPP_API.cmake) + message(STATUS "Got CCPP API from cmakefile include file: ${API}") +endif(API) + +set(CMAKE_Fortran_FLAGS "${CMAKE_Fortran_FLAGS} -O0 -fno-unsafe-math-optimizations -frounding-math -fsignaling-nans -ffpe-trap=invalid,zero,overflow -fbounds-check -ggdb -fbacktrace -ffree-line-length-none") + +#------------------------------------------------------------------------------ +add_library(ccpp_chunked_data STATIC ${SCHEMES} ${CAPS} ${API}) +# Generate list of Fortran modules from defined sources +foreach(source_f90 ${CAPS} ${API}) + get_filename_component(tmp_source_f90 ${source_f90} NAME) + string(REGEX REPLACE ".F90" ".mod" tmp_module_f90 ${tmp_source_f90}) + string(TOLOWER ${tmp_module_f90} module_f90) + list(APPEND MODULES_F90 ${CMAKE_CURRENT_BINARY_DIR}/${module_f90}) +endforeach() + +set_target_properties(ccpp_chunked_data PROPERTIES VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_VERSION_MAJOR}) + +add_executable(test_chunked_data.x main.F90) +add_dependencies(test_chunked_data.x ccpp_chunked_data) +target_link_libraries(test_chunked_data.x ccpp_chunked_data) +set_target_properties(test_chunked_data.x PROPERTIES LINKER_LANGUAGE Fortran) + +# Define where to install the library +install(TARGETS ccpp_chunked_data + EXPORT ccpp_chunked_data-targets + ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib + RUNTIME DESTINATION lib +) +# Export our configuration +install(EXPORT ccpp_chunked_data-targets + FILE ccpp_chunked_data-config.cmake + DESTINATION lib/cmake +) +# Define where to install the C headers and Fortran modules +#install(FILES ${HEADERS_C} DESTINATION include) +install(FILES ${MODULES_F90} DESTINATION include) diff --git a/test_prebuild/test_chunked_data/README.md b/test_prebuild/test_chunked_data/README.md new file mode 100644 index 00000000..40812515 --- /dev/null +++ b/test_prebuild/test_chunked_data/README.md @@ -0,0 +1,13 @@ +# How to build the chunked data test + +1. Set compiler environment as appropriate for your system +2. Run the following commands: +``` +cd test_prebuild/test_chunked_data/ +rm -fr build +mkdir build +../../scripts/ccpp_prebuild.py --config=ccpp_prebuild_config.py --builddir=build +cd build +cmake .. 2>&1 | tee log.cmake +make 2>&1 | tee log.make +``` diff --git a/test_prebuild/test_chunked_data/ccpp_prebuild_config.py b/test_prebuild/test_chunked_data/ccpp_prebuild_config.py new file mode 100755 index 00000000..4e32d37d --- /dev/null +++ b/test_prebuild/test_chunked_data/ccpp_prebuild_config.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python + +# CCPP prebuild config for GFDL Finite-Volume Cubed-Sphere Model (FV3) + +############################################################################### +# Definitions # +############################################################################### + +HOST_MODEL_IDENTIFIER = "FV3" + +# Add all files with metadata tables on the host model side and in CCPP, +# relative to basedir = top-level directory of host model. This includes +# kind and type definitions used in CCPP physics. Also add any internal +# dependencies of these files to the list. +VARIABLE_DEFINITION_FILES = [ + # actual variable definition files + '../../src/ccpp_types.F90', + 'data.F90', + ] + +TYPEDEFS_NEW_METADATA = { + 'ccpp_types' : { + 'ccpp_t' : 'cdata', + 'ccpp_types' : '', + }, + 'data' : { + 'chunked_data_type' : 'chunked_data_instance', + 'data' : '', + }, + } + +# Add all physics scheme files relative to basedir +SCHEME_FILES = [ + 'chunked_data_scheme.F90', + ] + +# Default build dir, relative to current working directory, +# if not specified as command-line argument +DEFAULT_BUILD_DIR = 'build' + +# Auto-generated makefile/cmakefile snippets that contain all type definitions +TYPEDEFS_MAKEFILE = '{build_dir}/CCPP_TYPEDEFS.mk' +TYPEDEFS_CMAKEFILE = '{build_dir}/CCPP_TYPEDEFS.cmake' +TYPEDEFS_SOURCEFILE = '{build_dir}/CCPP_TYPEDEFS.sh' + +# Auto-generated makefile/cmakefile snippets that contain all schemes +SCHEMES_MAKEFILE = '{build_dir}/CCPP_SCHEMES.mk' +SCHEMES_CMAKEFILE = '{build_dir}/CCPP_SCHEMES.cmake' +SCHEMES_SOURCEFILE = '{build_dir}/CCPP_SCHEMES.sh' + +# Auto-generated makefile/cmakefile snippets that contain all caps +CAPS_MAKEFILE = '{build_dir}/CCPP_CAPS.mk' +CAPS_CMAKEFILE = '{build_dir}/CCPP_CAPS.cmake' +CAPS_SOURCEFILE = '{build_dir}/CCPP_CAPS.sh' + +# Directory where to put all auto-generated physics caps +CAPS_DIR = '{build_dir}' + +# Directory where the suite definition files are stored +SUITES_DIR = '.' + +# Optional arguments - only required for schemes that use +# optional arguments. ccpp_prebuild.py will throw an exception +# if it encounters a scheme subroutine with optional arguments +# if no entry is made here. Possible values are: 'all', 'none', +# or a list of standard_names: [ 'var1', 'var3' ]. +OPTIONAL_ARGUMENTS = {} + +# Directory where to write static API to +STATIC_API_DIR = '{build_dir}' +STATIC_API_CMAKEFILE = '{build_dir}/CCPP_API.cmake' +STATIC_API_SOURCEFILE = '{build_dir}/CCPP_API.sh' + +# Directory for writing HTML pages generated from metadata files +METADATA_HTML_OUTPUT_DIR = '{build_dir}' + +# HTML document containing the model-defined CCPP variables +HTML_VARTABLE_FILE = '{build_dir}/CCPP_VARIABLES_CHUNKED_DATA.html' + +# LaTeX document containing the provided vs requested CCPP variables +LATEX_VARTABLE_FILE = '{build_dir}/CCPP_VARIABLES_CHUNKED_DATA.tex' diff --git a/test_prebuild/test_chunked_data/chunked_data_scheme.F90 b/test_prebuild/test_chunked_data/chunked_data_scheme.F90 new file mode 100644 index 00000000..1bb2a266 --- /dev/null +++ b/test_prebuild/test_chunked_data/chunked_data_scheme.F90 @@ -0,0 +1,118 @@ +!>\file chunked_data_scheme.F90 +!! This file contains a chunked_data_scheme CCPP scheme that does nothing +!! except requesting the minimum, mandatory variables. + +module chunked_data_scheme + + use, intrinsic :: iso_fortran_env, only: error_unit + implicit none + + private + public :: chunked_data_scheme_init, & + chunked_data_scheme_timestep_init, & + chunked_data_scheme_run, & + chunked_data_scheme_timestep_finalize, & + chunked_data_scheme_finalize + + ! This is for unit testing only + integer, parameter, dimension(4) :: data_array_sizes = (/6,6,6,3/) + + contains + +!! \section arg_table_chunked_data_scheme_init Argument Table +!! \htmlinclude chunked_data_scheme_init.html +!! + subroutine chunked_data_scheme_init(data_array, errmsg, errflg) + character(len=*), intent(out) :: errmsg + integer, intent(out) :: errflg + integer, intent(in) :: data_array(:) + ! Initialize CCPP error handling variables + errmsg = '' + errflg = 0 + ! Check size of data array + write(error_unit,'(a,i3)') 'In chunked_data_scheme_init: checking size of data array to be', sum(data_array_sizes) + if (size(data_array)/=sum(data_array_sizes)) then + write(errmsg,'(2(a,i3))') "Error, expected size(data_array)==", sum(data_array_sizes), "but got ", size(data_array) + errflg = 1 + return + end if + end subroutine chunked_data_scheme_init + +!! \section arg_table_chunked_data_scheme_timestep_init Argument Table +!! \htmlinclude chunked_data_scheme_timestep_init.html +!! + subroutine chunked_data_scheme_timestep_init(data_array, errmsg, errflg) + character(len=*), intent(out) :: errmsg + integer, intent(out) :: errflg + integer, intent(in) :: data_array(:) + ! Initialize CCPP error handling variables + errmsg = '' + errflg = 0 + ! Check size of data array + write(error_unit,'(a,i3)') 'In chunked_data_scheme_timestep_init: checking size of data array to be', sum(data_array_sizes) + if (size(data_array)/=sum(data_array_sizes)) then + write(errmsg,'(2(a,i3))') "Error, expected size(data_array)==", sum(data_array_sizes), " but got ", size(data_array) + errflg = 1 + return + end if + end subroutine chunked_data_scheme_timestep_init + +!! \section arg_table_chunked_data_scheme_run Argument Table +!! \htmlinclude chunked_data_scheme_run.html +!! + subroutine chunked_data_scheme_run(nchunk, data_array, errmsg, errflg) + character(len=*), intent(out) :: errmsg + integer, intent(out) :: errflg + integer, intent(in) :: nchunk + integer, intent(in) :: data_array(:) + ! Initialize CCPP error handling variables + errmsg = '' + errflg = 0 + ! Check size of data array + write(error_unit,'(2(a,i3))') 'In chunked_data_scheme_run: checking size of data array for chunk', nchunk, ' to be', data_array_sizes(nchunk) + if (size(data_array)/=data_array_sizes(nchunk)) then + write(errmsg,'(a,i4)') "Error in chunked_data_scheme_run, expected size(data_array)==6, got ", size(data_array) + errflg = 1 + return + end if + end subroutine chunked_data_scheme_run + + !! \section arg_table_chunked_data_scheme_timestep_finalize Argument Table + !! \htmlinclude chunked_data_scheme_timestep_finalize.html + !! + subroutine chunked_data_scheme_timestep_finalize(data_array, errmsg, errflg) + character(len=*), intent(out) :: errmsg + integer, intent(out) :: errflg + integer, intent(in) :: data_array(:) + ! Initialize CCPP error handling variables + errmsg = '' + errflg = 0 + ! Check size of data array + write(error_unit,'(a,i3)') 'In chunked_data_scheme_timestep_finalize: checking size of data array to be', sum(data_array_sizes) + if (size(data_array)/=sum(data_array_sizes)) then + write(errmsg,'(2(a,i3))') "Error, expected size(data_array)==", sum(data_array_sizes), "but got ", size(data_array) + errflg = 1 + return + end if + end subroutine chunked_data_scheme_timestep_finalize + +!! \section arg_table_chunked_data_scheme_finalize Argument Table +!! \htmlinclude chunked_data_scheme_finalize.html +!! + subroutine chunked_data_scheme_finalize(data_array, errmsg, errflg) + character(len=*), intent(out) :: errmsg + integer, intent(out) :: errflg + integer, intent(in) :: data_array(:) + ! Initialize CCPP error handling variables + errmsg = '' + errflg = 0 + ! Check size of data array + write(error_unit,'(a,i3)') 'In chunked_data_scheme_finalize: checking size of data array to be', sum(data_array_sizes) + if (size(data_array)/=sum(data_array_sizes)) then + write(errmsg,'(2(a,i3))') "Error, expected size(data_array)==", sum(data_array_sizes), "but got ", size(data_array) + errflg = 1 + return + end if + end subroutine chunked_data_scheme_finalize + +end module chunked_data_scheme diff --git a/test_prebuild/test_chunked_data/chunked_data_scheme.meta b/test_prebuild/test_chunked_data/chunked_data_scheme.meta new file mode 100644 index 00000000..13830dbf --- /dev/null +++ b/test_prebuild/test_chunked_data/chunked_data_scheme.meta @@ -0,0 +1,147 @@ +[ccpp-table-properties] + name = chunked_data_scheme + type = scheme + dependencies = + +######################################################################## +[ccpp-arg-table] + name = chunked_data_scheme_init + type = scheme +[errmsg] + standard_name = ccpp_error_message + long_name = error message for error handling in CCPP + units = none + dimensions = () + type = character + kind = len=* + intent = out +[errflg] + standard_name = ccpp_error_code + long_name = error code for error handling in CCPP + units = 1 + dimensions = () + type = integer + intent = out +[data_array] + standard_name = chunked_data_array + long_name = chunked data array + units = 1 + dimensions = (horizontal_dimension) + type = integer + intent = in + +######################################################################## +[ccpp-arg-table] + name = chunked_data_scheme_timestep_init + type = scheme +[errmsg] + standard_name = ccpp_error_message + long_name = error message for error handling in CCPP + units = none + dimensions = () + type = character + kind = len=* + intent = out +[errflg] + standard_name = ccpp_error_code + long_name = error code for error handling in CCPP + units = 1 + dimensions = () + type = integer + intent = out +[data_array] + standard_name = chunked_data_array + long_name = chunked data array + units = 1 + dimensions = (horizontal_dimension) + type = integer + intent = in + +######################################################################## +[ccpp-arg-table] + name = chunked_data_scheme_run + type = scheme +[errmsg] + standard_name = ccpp_error_message + long_name = error message for error handling in CCPP + units = none + dimensions = () + type = character + kind = len=* + intent = out +[errflg] + standard_name = ccpp_error_code + long_name = error code for error handling in CCPP + units = 1 + dimensions = () + type = integer + intent = out +[nchunk] + standard_name = ccpp_chunk_number + long_name = number of chunk for chunked arrays in CCPP + units = index + dimensions = () + type = integer + intent = in +[data_array] + standard_name = chunked_data_array + long_name = chunked data array + units = 1 + dimensions = (horizontal_loop_extent) + type = integer + intent = in + +######################################################################## +[ccpp-arg-table] + name = chunked_data_scheme_timestep_finalize + type = scheme +[errmsg] + standard_name = ccpp_error_message + long_name = error message for error handling in CCPP + units = none + dimensions = () + type = character + kind = len=* + intent = out +[errflg] + standard_name = ccpp_error_code + long_name = error code for error handling in CCPP + units = 1 + dimensions = () + type = integer + intent = out +[data_array] + standard_name = chunked_data_array + long_name = chunked data array + units = 1 + dimensions = (horizontal_dimension) + type = integer + intent = in + +######################################################################## +[ccpp-arg-table] + name = chunked_data_scheme_finalize + type = scheme +[errmsg] + standard_name = ccpp_error_message + long_name = error message for error handling in CCPP + units = none + dimensions = () + type = character + kind = len=* + intent = out +[errflg] + standard_name = ccpp_error_code + long_name = error code for error handling in CCPP + units = 1 + dimensions = () + type = integer + intent = out +[data_array] + standard_name = chunked_data_array + long_name = chunked data array + units = 1 + dimensions = (horizontal_dimension) + type = integer + intent = in + diff --git a/test_prebuild/test_chunked_data/data.F90 b/test_prebuild/test_chunked_data/data.F90 new file mode 100644 index 00000000..25518bef --- /dev/null +++ b/test_prebuild/test_chunked_data/data.F90 @@ -0,0 +1,43 @@ +module data + +!! \section arg_table_data Argument Table +!! \htmlinclude data.html +!! + use ccpp_types, only: ccpp_t + + implicit none + + private + + public nchunks, chunksize, chunk_begin, chunk_end, ncols + public ccpp_data_domain, ccpp_data_chunks, chunked_data_type, chunked_data_instance + + integer, parameter :: nchunks = 4 + type(ccpp_t), target :: ccpp_data_domain + type(ccpp_t), dimension(nchunks), target :: ccpp_data_chunks + + integer, parameter, dimension(nchunks) :: chunksize = (/6,6,6,3/) + integer, parameter, dimension(nchunks) :: chunk_begin = (/1,7,13,19/) + integer, parameter, dimension(nchunks) :: chunk_end = (/6,12,18,21/) + integer, parameter :: ncols = sum(chunksize) + +!! \section arg_table_chunked_data_type +!! \htmlinclude chunked_data_type.html +!! + type chunked_data_type + integer, dimension(:), allocatable :: array_data + contains + procedure :: create => chunked_data_create + end type chunked_data_type + + type(chunked_data_type) :: chunked_data_instance + +contains + + subroutine chunked_data_create(chunked_data_instance, ncol) + class(chunked_data_type), intent(inout) :: chunked_data_instance + integer, intent(in) :: ncol + allocate(chunked_data_instance%array_data(ncol)) + end subroutine chunked_data_create + +end module data diff --git a/test_prebuild/test_chunked_data/data.meta b/test_prebuild/test_chunked_data/data.meta new file mode 100644 index 00000000..c14217df --- /dev/null +++ b/test_prebuild/test_chunked_data/data.meta @@ -0,0 +1,76 @@ +[ccpp-table-properties] + name = chunked_data_type + type = ddt + dependencies = +[ccpp-arg-table] + name = chunked_data_type + type = ddt +[array_data] + standard_name = chunked_data_array + long_name = chunked data array + units = 1 + dimensions = (ccpp_constant_one:horizontal_dimension) + type = integer +# Todo: define an additional array running from -1 to ncols-2 + +[ccpp-table-properties] + name = data + type = module + dependencies = +[ccpp-arg-table] + name = data + type = module +[cdata] + standard_name = ccpp_t_instance + long_name = instance of derived data type ccpp_t + units = DDT + dimensions = () + type = ccpp_t +[ncols] + standard_name = horizontal_dimension + long_name = horizontal dimension + units = count + dimensions = () + type = integer +[nchunks] + standard_name = ccpp_chunk_extent + long_name = number of chunks of array data used in run phase + units = count + dimensions = () + type = integer +[chunk_begin] + standard_name = horizontal_loop_begin_all_chunks + long_name = first index for horizontal loop extent in run phase + units = index + dimensions = (ccpp_chunk_extent) + type = integer +[chunk_begin(ccpp_chunk_number)] + standard_name = horizontal_loop_begin + long_name = first index for horizontal loop extent in run phase + units = index + dimensions = () + type = integer +[chunk_end] + standard_name = horizontal_loop_end_all_chunks + long_name = last index for horizontal loop extent in run phase + units = index + dimensions = (ccpp_chunk_extent) + type = integer +[chunk_end(ccpp_chunk_number)] + standard_name = horizontal_loop_end + long_name = last index for horizontal loop extent in run phase + units = index + dimensions = () + type = integer +[chunked_data_type] + standard_name = chunked_data_type + long_name = definition of type chunked_data_type + units = DDT + dimensions = () + type = chunked_data_type +[chunked_data_instance] + standard_name = chunked_data_type_instance + long_name = instance of derived data type chunked_data_type + units = DDT + dimensions = () + type = chunked_data_type diff --git a/test_prebuild/test_chunked_data/main.F90 b/test_prebuild/test_chunked_data/main.F90 new file mode 100644 index 00000000..cad6f2b6 --- /dev/null +++ b/test_prebuild/test_chunked_data/main.F90 @@ -0,0 +1,108 @@ +program test_chunked_data + + use, intrinsic :: iso_fortran_env, only: error_unit + + use ccpp_types, only: ccpp_t + use data, only: nchunks, chunksize, ncols + use data, only: ccpp_data_domain, ccpp_data_chunks, & + chunked_data_type, chunked_data_instance + + use ccpp_static_api, only: ccpp_physics_init, & + ccpp_physics_timestep_init, & + ccpp_physics_run, & + ccpp_physics_timestep_finalize, & + ccpp_physics_finalize + + implicit none + + character(len=*), parameter :: ccpp_suite = 'chunked_data_suite' + integer :: ic, ierr + type(ccpp_t), pointer :: cdata => null() + + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + ! CCPP init step ! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + ! For physics running over the entire domain, + ! ccpp_thread_number and ccpp_chunk_number are + ! set to 0, indicating that arrays are to be sent + ! following their dimension specification in the + ! metadata (must match horizontal_dimension). + ccpp_data_domain%thrd_no = 0 + ccpp_data_domain%chunk_no = 0 + + ! Loop over all chunks and threads for ccpp_data_chunks + do ic=1,nchunks + ! Assign the correct chunk numbers, only one thread + ccpp_data_chunks(ic)%chunk_no = ic + ccpp_data_chunks(ic)%thrd_no = 1 + end do + + call chunked_data_instance%create(ncols) + write(error_unit,'(2(a,i3))') "Chunked_data_instance%array_data to size", size(chunked_data_instance%array_data) + + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + ! CCPP physics init step ! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + cdata => ccpp_data_domain + call ccpp_physics_init(cdata, suite_name=trim(ccpp_suite), ierr=ierr) + if (ierr/=0) then + write(error_unit,'(a)') "An error occurred in ccpp_physics_init:" + write(error_unit,'(a)') trim(cdata%errmsg) + stop 1 + end if + + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + ! CCPP physics timestep init step ! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + cdata => ccpp_data_domain + call ccpp_physics_timestep_init(cdata, suite_name=trim(ccpp_suite), ierr=ierr) + if (ierr/=0) then + write(error_unit,'(a)') "An error occurred in ccpp_physics_timestep_init:" + write(error_unit,'(a)') trim(cdata%errmsg) + stop 1 + end if + + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + ! CCPP physics run step ! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + do ic=1,nchunks + cdata => ccpp_data_chunks(ic) + call ccpp_physics_run(cdata, suite_name=trim(ccpp_suite), ierr=ierr) + if (ierr/=0) then + write(error_unit,'(a,i3,a)') "An error occurred in ccpp_physics_run for chunk", ic, ":" + write(error_unit,'(a)') trim(cdata%errmsg) + stop 1 + end if + end do + + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + ! CCPP physics timestep finalize step ! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + cdata => ccpp_data_domain + call ccpp_physics_timestep_finalize(cdata, suite_name=trim(ccpp_suite), ierr=ierr) + if (ierr/=0) then + write(error_unit,'(a)') "An error occurred in ccpp_physics_timestep_init:" + write(error_unit,'(a)') trim(cdata%errmsg) + stop 1 + end if + + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + ! CCPP physics finalize step ! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + cdata => ccpp_data_domain + call ccpp_physics_finalize(cdata, suite_name=trim(ccpp_suite), ierr=ierr) + if (ierr/=0) then + write(error_unit,'(a)') "An error occurred in ccpp_physics_timestep_init:" + write(error_unit,'(a)') trim(cdata%errmsg) + stop 1 + end if + +contains + +end program test_chunked_data \ No newline at end of file diff --git a/test_prebuild/test_chunked_data/suite_chunked_data_suite.xml b/test_prebuild/test_chunked_data/suite_chunked_data_suite.xml new file mode 100644 index 00000000..923e5fb7 --- /dev/null +++ b/test_prebuild/test_chunked_data/suite_chunked_data_suite.xml @@ -0,0 +1,9 @@ + + + + + + chunked_data_scheme + + +