diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md new file mode 100644 index 00000000000..121eed3e933 --- /dev/null +++ b/.claude/CLAUDE.md @@ -0,0 +1,11 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +@../AGENTS.md + +# Important +This CLAUDE.md file is maintained by the project maintainers and should not be +modified by /init or any automated process. Do not overwrite, replace, or +regenerate this file. If asked to run /init, warn the user that it will +overwrite the project's curated AI instructions. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index af60457e340..c132f3bf736 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -5,7 +5,7 @@ Please provide a clear and concise description of what the problem is e.g. I'm a ## Describe the solution you'd like ## Describe alternative solutions diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 84e9337a3d4..02521c6f349 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -7,9 +7,9 @@ - Closes # ## Checklist -- [ ] My code follows the style guidlines of this proejct (black formatting) +- [ ] My code follows the style guidelines of this project (black formatting) - [ ] I have performed a self-review of my own code - [ ] My changes generate no new warnings -- [ ] I have added tests that excerise my feature/fix and existing tests continue to pass +- [ ] I have added tests that exercise my feature/fix and existing tests continue to pass - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding additions and changes to the documentation diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index bd6e288cae2..3ddf29c1663 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -40,6 +40,8 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 + with: + submodules: "true" - name: Set up QEMU uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx @@ -62,8 +64,8 @@ jobs: - name: Build and push uses: docker/build-push-action@v6 with: - target: base - context: docker/ + file: docker/Dockerfile + context: . push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} @@ -99,7 +101,7 @@ jobs: strategy: fail-fast: false matrix: - python-version: ["3.8", "3.10", "3.12"] + python-version: ["3.9", "3.10", "3.12", "3.13", "3.14"] steps: - name: Checkout model code uses: actions/checkout@v4 @@ -123,6 +125,8 @@ jobs: with: path: cesm/cime submodules: "true" + - name: Install uv + uses: astral-sh/setup-uv@v7 - name: Run tests shell: bash working-directory: cesm/cime @@ -132,18 +136,15 @@ jobs: CIME_TEST_PLATFORM: "ubuntu-latest" SKIP_ENTRYPOINT: "true" run: | - source /opt/conda/etc/profile.d/conda.sh - - conda activate cesm - - mamba install -y 'python=${{ matrix.python-version }}' - - pip install -r test-requirements.txt + # manually run the entrypoint + source /entrypoint.sh - # GitHub runner home is different than container - cp -rf /home/cime/.cime /github/home/ + uv python install "${{ matrix.python-version }}" + uv venv + source .venv/bin/activate + uv pip install -r test-requirements.txt - pytest -vvv --cov=CIME --cov-branch --cov-report=xml --machine docker CIME/tests/test_unit* + uv run pytest -vvv --cov=CIME --cov-branch --cov-report=xml --machine docker CIME/tests/test_unit* - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v4 with: @@ -212,8 +213,12 @@ jobs: - name: Cache inputdata uses: actions/cache@v3 with: - path: /home/cime/inputdata + path: /root/storage/inputdata key: inputdata-2 + - name: Install uv + uses: astral-sh/setup-uv@v7 + with: + python-version: "3.10" - name: Run tests shell: bash working-directory: ${{ matrix.model.name }}/cime @@ -223,31 +228,18 @@ jobs: CIME_TEST_PLATFORM: ubuntu-latest SKIP_ENTRYPOINT: "true" run: | - source /opt/conda/etc/profile.d/conda.sh - - conda activate ${{ matrix.model.name }} - - pip install -r test-requirements.txt - + # manually run the entrypoint source /entrypoint.sh - if [[ "${{ matrix.model.name }}" == "e3sm" ]]; then - fix_mct_makefiles ../externals/mct - fi - - # GitHub runner home is different than container - cp -rf /home/cime/.cime /github/home/ - - if [[ "${CIME_MODEL}" == "e3sm" ]]; then - ln -sf /github/home/.cime/config_machines.v2.xml /github/home/.cime/config_machines.xml - else - ln -sf /github/home/.cime/config_machines.v3.xml /github/home/.cime/config_machines.xml - fi + uv python install 3.10 + uv venv + source .venv/bin/activate + uv pip install -r test-requirements.txt git config --global user.name "${{ github.actor }}" git config --global user.email "${{ github.actor }}@users.noreply.github.com" - pytest -vvv --cov=CIME --cov-branch --cov-report=xml --machine docker --no-fortran-run --no-teardown CIME/tests/test_sys* + uv run pytest -vvv --cov=CIME --cov-branch --cov-report=xml --machine docker --no-fortran-run --no-teardown CIME/tests/test_sys* - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v4 with: @@ -259,7 +251,7 @@ jobs: - name: Create testing log archive if: ${{ failure() }} shell: bash - run: tar -czvf /testing-logs-${GITHUB_RUN_NUMBER}-${{ matrix.model.name }}-${{ matrix.driver }}.tar.gz /home/cime/cases /home/cime/baselines /home/cime/archive + run: tar -czvf /testing-logs-${GITHUB_RUN_NUMBER}-${{ matrix.model.name }}-${{ matrix.driver }}.tar.gz /root/storage/cases /root/storage/baselines /root/storage/archive # How to download artifacts: # https://docs.github.com/en/actions/managing-workflow-runs/downloading-workflow-artifacts - name: Upload testing logs diff --git a/.gitignore b/.gitignore index a1c5112c573..978286a820e 100644 --- a/.gitignore +++ b/.gitignore @@ -23,9 +23,16 @@ scripts/Tools/JENKINS* # Ignore anything that are produced under scripts "cases" directory /scripts/cases/ -#Ignore Externals -components -libraries -share +# Ignore some other files test_coverage/** *.bak + +# AI coding tool local state +.gemini/ +.codex/ + +# Special for Claude Code user config +.claude/* +!.claude/CLAUDE.md +CLAUDE.md +CLAUDE.local.md diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c478a540731..442206da5c2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -18,7 +18,7 @@ repos: - id: black files: CIME - repo: https://github.com/PyCQA/pylint - rev: v2.11.1 + rev: v4.0.4 hooks: - id: pylint args: diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000000..75f517e91a1 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,173 @@ +# AGENTS.md + +This file provides guidance to AI agents when working with code in this repository. + +## Note to AI agents + +This AGENTS.md is maintained by the CIME project. Do not overwrite or +regenerate this file with init commands. + +## Note to claude code users + +A CLAUDE.md file is in .claude directory. It includes +this file. Ignore tips to run init. + +## Project Overview + +CIME (Common Infrastructure for Modeling the Earth) provides a Case Control System (CCS) for configuring, compiling, and executing Earth System Models, plus a framework for system testing. CIME is a Python-based infrastructure currently used by CESM, E3SM, NorESM and other models. It does NOT contain model source code itself, but provides the infrastructure to manage model runs. + +## Running Tests + +### Unit and System Tests + +From the repository root, run tests using either: + +```bash +# Using pytest (recommended) +pytest CIME/tests + +# Run specific test file +pytest CIME/tests/test_unit_foo.py + +# Run specific test class +pytest CIME/tests/test_unit_foo.py::TestClass + +# Run specific test case +pytest CIME/tests/test_unit_foo.py::TestClass::test_method +``` + +Test files follow a naming convention: +- Unit tests: `test_unit_*.py` +- System tests: `test_sys_*.py` + +### Pre-commit Hooks + +Before committing, always run: + +```bash +pip install pre-commit +pre-commit run -a +``` + +This runs: +- `black` formatter on CIME code +- `pylint` with project-specific configuration +- XML validation on config files +- End-of-file and trailing whitespace checks + +## Code Quality + +- Code is formatted with `black` +- Linted with `pylint` (see `.pre-commit-config.yaml` for disabled checks) +- Python 3.9+ required +- Follow PEP8 style guidelines + +## Key Architecture Concepts + +### Case Control System (CCS) + +The heart of CIME is the `Case` class (`CIME/case/case.py`), which manages all interactions with a CIME case. The Case class coordinates between: + +1. **Config XML Classes** (readonly) - Located in `CIME/XML/`, these read CIME distribution config files like `config_*.xml`. Python classes are named after the XML they read (e.g., `Machines` reads machine configs). + +2. **Env XML Classes** (read/write) - Also in `CIME/XML/`, these manage case-specific `env_*.xml` files. Classes are named `Env*` (e.g., `EnvRun`, `EnvBuild`). + +The Case class contains an array of Env classes and uses Config classes to populate them during case creation/configuration. + +### Directory Structure + +``` +CIME/ +├── case/ # Case control modules (setup, run, submit, etc.) +├── XML/ # XML parsers for config and env files +├── SystemTests/ # System test implementations (ERS, ERT, etc.) +├── Tools/ # Case manipulation tools (xmlchange, xmlquery, etc.) +├── scripts/ # Top-level user-facing scripts +├── data/ # Config files, XML schemas +├── tests/ # Unit and system tests +├── BuildTools/ # Build system utilities +└── non_py/ # Non-Python components (C/Fortran) + +scripts/ +├── create_newcase # Create new case +├── create_test # Create and run tests +├── create_clone # Clone existing case +├── query_config # Query available configurations +└── query_testlists # Query test lists + +tools/ +└── mapping/ # Grid mapping file generation tools +``` + +### Common Workflows + +**Create a case** (requires machine configuration): +```bash +./scripts/create_newcase --case CASENAME --compset COMPSET --res GRID [--machine MACHINE] +``` + +**Create and run tests**: +```bash +./scripts/create_test TESTNAME +./scripts/create_test TESTNAME1 TESTNAME2 ... +./scripts/create_test -f TESTFILE # from file +``` + +**Query configurations**: +```bash +./scripts/query_config --compsets +./scripts/query_config --grids +./scripts/query_config --machines +``` + +### Model System Tests + +These are tests of properties of the model CIME is included in. +System tests inherit from `SystemTestsCommon` base class (`CIME/SystemTests/system_tests_common.py`). Common test types: +- **ERS**: Exact restart test +- **ERT**: Exact restart with different threading +- **SMS**: Smoke test +- **SEQ**: Sequencing test + +Each test type has its own module in `CIME/SystemTests/`. + +### XML-Based Configuration + +CIME is heavily XML-driven. Key concepts: +- Generic XML handling is in `CIME/XML/generic_xml.py` +- All XML classes inherit from `GenericXML` +- XML schemas are in `CIME/data/config/xml_schemas/` +- Config files define machines, compsets, grids, tests + +### Case Management Tools + +Located in `CIME/Tools/`, these are executable scripts: +- `xmlchange`: Modify case XML variables +- `xmlquery`: Query case XML variables +- `case.setup`: Setup case directory structure +- `case.build`: Build the case +- `case.submit`: Submit case to batch system +- `preview_namelists`: Generate and preview namelists + +## Documentation + +Build Sphinx documentation: +```bash +cd doc +make clean +make api +make html +``` + +Requires: `sphinx`, `sphinxcontrib-programoutput`, and custom theme (see `doc/README`). + +Online documentation: https://esmci.github.io/cime + +## Development Notes + +- When modifying Case env classes, changes affect the case's XML files +- The Case class extends across multiple files using imports (see imports at end of `case.py`) +- CIME must be integrated with host models (CESM, E3SM, NorESM) to run Model System Tests +on a supported machine (found using `./scripts/query_config --machines`)/ +- Machine-specific configurations are in XML files, not hardcoded +- Git submodules may need initialization: `git submodule update --init` diff --git a/CIME/Servers/wget.py b/CIME/Servers/wget.py index 56517cffa7b..fde411460e8 100644 --- a/CIME/Servers/wget.py +++ b/CIME/Servers/wget.py @@ -26,20 +26,21 @@ def wget_login(cls, address, user="", passwd=""): args += "--password {} ".format(passwd) try: - err = run_cmd("wget {} --spider {}".format(args, address), timeout=60)[0] - except: + err, _, errstr = run_cmd( + "wget {} --spider {}".format(args, address), timeout=60 + ) + except RuntimeError: logger.warning( - "Could not connect to repo '{0}'\nThis is most likely either a proxy, or network issue .(location 1)".format( + "Could not connect to repo '{0}'\nThis is most likely either a proxy, or network issue .".format( address ) ) return None - - if err and not "storage.neonscience.org" in address: + if "Connecting to " in errstr and "... connected" in errstr: + logger.warning("Connection established with nonzero code %s", err) + elif err: logger.warning( - "Could not connect to repo '{0}'\nThis is most likely either a proxy, or network issue .(location 2)".format( - address - ) + "Could not connect to repo '{0}'\n{1}".format(address, errstr) ) return None diff --git a/CIME/SystemTests/eri.py b/CIME/SystemTests/eri.py index 6d80a8fd808..f0a237b6819 100644 --- a/CIME/SystemTests/eri.py +++ b/CIME/SystemTests/eri.py @@ -90,7 +90,11 @@ def run_phase(self): start_1_year, start_1_month, start_1_day = [ int(item) for item in start_1.split("-") ] - start_2_year = start_1_year + 2 + # Change the year for the hybrid case to make sure the system can handle this change in year. + # Note: When using a Gregorian calendar, it is important for the two years to be leap years + # because a testmod which tests the gregorian calendar starts the runs on leap day, + # which must exist in all of the start years. + start_2_year = start_1_year + 4 start_2 = "{:04d}-{:02d}-{:02d}".format( start_2_year, start_1_month, start_1_day ) @@ -268,7 +272,7 @@ def run_phase(self): self._skip_pnl = False # run branch case (short term archiving is off) - self.run_indv() + self.run_indv(suffix="branch") # # (3b) Test run: @@ -298,5 +302,17 @@ def run_phase(self): # do the restart run (short term archiving is off) self.run_indv(suffix="rest") - self._component_compare_test("base", "hybrid") - self._component_compare_test("base", "rest") + # Note that, for both of these comparisons, the "test" case comes first and the + # "control" case comes second: the branch case is compared against the hybrid case + # (which it branched off of, and so serves as its "control"); the "rest" run is a + # restart from the branch case and so is compared against this branch case. We + # make this choice because the cprnc output file names are derived from the first + # suffix, so: + # - Listing the "test" case as the first suffix means that the cprnc files are + # named with the name of the case we're testing in that cprnc comparison, which + # is more intuitive. + # - Having the first suffix differ between the two comparisons is important to + # avoid having the cprnc output files from the second comparison overwrite the + # files from the first comparison. + self._component_compare_test("branch", "hybrid") + self._component_compare_test("rest", "branch") diff --git a/CIME/SystemTests/funit.py b/CIME/SystemTests/funit.py index a7c21a06944..7681a6200d6 100644 --- a/CIME/SystemTests/funit.py +++ b/CIME/SystemTests/funit.py @@ -35,6 +35,13 @@ def get_test_spec_dir(self): """ return get_cime_root() + def get_extra_run_tests_args(self): + """ + Override this to return a string containing extra command-line arguments to + run_tests.py + """ + return "" + def run_phase(self): rundir = self._case.get_value("RUNDIR") @@ -51,8 +58,9 @@ def run_phase(self): get_cime_root(), "scripts", "fortran_unit_testing", "run_tests.py" ) ) - args = "--build-dir {} --test-spec-dir {} --machine {}".format( - exeroot, test_spec_dir, mach + extra_args = self.get_extra_run_tests_args() + args = "--build-dir {} --test-spec-dir {} --machine {} {}".format( + exeroot, test_spec_dir, mach, extra_args ) stat = run_cmd( diff --git a/CIME/SystemTests/hommebaseclass.py b/CIME/SystemTests/hommebaseclass.py index 8ea4a03c6b9..d275553a545 100644 --- a/CIME/SystemTests/hommebaseclass.py +++ b/CIME/SystemTests/hommebaseclass.py @@ -1,11 +1,12 @@ """ CIME HOMME test. This class inherits from SystemTestsCommon """ + from CIME.XML.standard_module_setup import * from CIME.SystemTests.system_tests_common import SystemTestsCommon from CIME.build import post_build from CIME.status import append_testlog -from CIME.utils import SharedArea +from CIME.utils import SharedArea, new_lid, gzip_existing_file from CIME.test_status import * import shutil @@ -19,6 +20,8 @@ def __init__(self, case, **kwargs): initialize an object interface to the SMS system test """ SystemTestsCommon.__init__(self, case, **kwargs) + compiler = case.get_value("COMPILER") + logger.info(f"[DEBUG] Detected compiler: {compiler}") case.load_env() self.csnd = "not defined" self.cmakesuffix = self.csnd @@ -36,6 +39,11 @@ def build_phase(self, sharedlib_only=False, model_only=False): gmake = self._case.get_value("GMAKE") gmake_j = self._case.get_value("GMAKE_J") cprnc = self._case.get_value("CCSM_CPRNC") + is_debug = self._case.get_value("DEBUG") + + # Assign LID at start + lid = new_lid(case=self._case) + log_path = os.path.join(exeroot, f"homme.bldlog.{lid}") if compare: basename = basecmp @@ -49,29 +57,45 @@ def build_phase(self, sharedlib_only=False, model_only=False): "ERROR in hommebaseclass: Must have cmakesuffix set up", ) + logger.info( + f"[DEBUG] Machine: {mach}, Source root: {srcroot}, Total Procs: {procs}, Is Debug: {is_debug}" + ) + logger.info("[DEBUG] Preparing to invoke CMake with constructed command:") cmake_cmd = "cmake -C {0}/components/homme/cmake/machineFiles/{1}{6}.cmake -DUSE_NUM_PROCS={2} {0}/components/homme -DHOMME_BASELINE_DIR={3}/{4} -DCPRNC_DIR={5}/..".format( srcroot, mach, procs, baselinedir, basename, cprnc, self.cmakesuffix ) + if is_debug: + cmake_cmd += " -DCMAKE_BUILD_TYPE=Debug" + run_cmd_no_fail( cmake_cmd, - arg_stdout="homme.bldlog", + arg_stdout=log_path, combine_output=True, from_dir=exeroot, ) run_cmd_no_fail( "{} -j{} VERBOSE=1 test-execs".format(gmake, gmake_j), - arg_stdout="homme.bldlog", + arg_stdout=log_path, combine_output=True, from_dir=exeroot, ) - post_build( - self._case, [os.path.join(exeroot, "homme.bldlog")], build_complete=True - ) + post_build(self._case, [log_path], build_complete=True) - def run_phase(self): + # Prepend the LID as first line if log file exists + if os.path.exists(log_path): + with open(log_path, "r") as original: + original_content = original.read() + with open(log_path, "w") as modified: + modified.write(f"LID: {lid}\n" + original_content) + gzip_existing_file(log_path) + else: + logger.warning( + f"[WARN] Build log does not exist: {log_path}. Skipping LID prepend and gzip step." + ) + def run_phase(self): rundir = self._case.get_value("RUNDIR") exeroot = self._case.get_value("EXEROOT") baseline = self._case.get_value("BASELINE_ROOT") @@ -80,9 +104,8 @@ def run_phase(self): basegen = self._case.get_value("BASEGEN_CASE") gmake = self._case.get_value("GMAKE") - log = os.path.join(rundir, "homme.log") - if os.path.exists(log): - os.remove(log) + lid = new_lid(case=self._case) + log = os.path.join(rundir, "homme.log." + lid) if generate: full_baseline_dir = os.path.join(baseline, basegen, "tests", "baseline") @@ -130,6 +153,10 @@ def run_phase(self): # is pretty useless for this test. append_testlog(open(log, "r").read()) + if stat == 0: + # Gzip the log file to be consistent with other CIME cases (bldglogs) + gzip_existing_file(log) + expect(stat == 0, "RUN FAIL for HOMME") # Homme is a bit of an oddball test since it's not really running the E3SM model diff --git a/CIME/SystemTests/system_tests_common.py b/CIME/SystemTests/system_tests_common.py index dcdb053ff39..7676b910c36 100644 --- a/CIME/SystemTests/system_tests_common.py +++ b/CIME/SystemTests/system_tests_common.py @@ -185,6 +185,8 @@ def _set_restart_interval( elif ncpl_base_period == "decade": coupling_secs = 315360000 / maxncpl timestep = 315360000 / minncpl + else: + raise CIMEError("unhandled ncpl_base_period value") # Convert stop_n to units of coupling intervals factor = 1 @@ -265,6 +267,7 @@ def _set_restart_interval( elif stop_option == "nyears": rtd = timedelta(days=rest_n * 365) else: + rtd = None expect(False, f"stop_option {stop_option} not available for this test") restdatetime = startdatetime + rtd @@ -1022,6 +1025,10 @@ def _compare_baseline(self): ts_comments = ( os.path.dirname(baseline_name) + ": " + get_ts_synopsis(comments) ) + log_comments = "\n\n============ BASELINE COMPARE SYNOPSIS =============\n" + log_comments += ts_comments + "\n" + log_comments += "====================================================\n" + append_testlog(log_comments, self._orig_caseroot) self._test_status.set_status(BASELINE_PHASE, status, comments=ts_comments) def _generate_baseline(self): @@ -1299,12 +1306,12 @@ def run_indv( class TESTRUNFAILEXC(TESTRUNPASS): def run_phase(self): - raise RuntimeError("Exception from run_phase") + raise CIMEError("Exception from run_phase") class TESTRUNSTARCFAIL(TESTRUNPASS): def _st_archive_case_test(self): - raise RuntimeError("Exception from st archive") + raise CIMEError("Exception from st archive") class TESTBUILDFAIL(TESTRUNPASS): @@ -1326,7 +1333,7 @@ def build_phase(self, sharedlib_only=False, model_only=False): class TESTBUILDFAILEXC(FakeTest): def build_phase(self, sharedlib_only=False, model_only=False): - raise RuntimeError("Exception from build") + raise CIMEError("Exception from build") class TESTRUNUSERXMLCHANGE(FakeTest): diff --git a/CIME/Tools/pelayout b/CIME/Tools/pelayout index 48a13a4cc91..d4c3575359b 100755 --- a/CIME/Tools/pelayout +++ b/CIME/Tools/pelayout @@ -47,6 +47,7 @@ import re logger = logging.getLogger(__name__) + ############################################################################### def parse_command_line(args, description): ############################################################################### @@ -70,7 +71,7 @@ def parse_command_line(args, description): parser.add_argument( "--format", - default="%4C: %6T/%6H; %6R %6P", + default="%4C: %6T/%6H; %6R %6P %6X", help="Format the PE layout items for each component (see below)", ) @@ -116,26 +117,39 @@ def get_value_as_string(case, var, attribute=None, resolved=False, subgroup=None ############################################################################### -def format_pelayout(comp, ntasks, nthreads, rootpe, pstride, arg_format): +def format_pelayout(comp, ntasks, nthreads, rootpe, pstride, xstride, arg_format): ############################################################################### """ Format the PE layout information for each component, using a default format, or using the arg_format input, if it exists. """ - subs = {"C": comp, "T": ntasks, "H": nthreads, "R": rootpe, "P": pstride} + subs = { + "C": comp, + "T": ntasks, + "H": nthreads, + "R": rootpe, + "P": pstride, + "X": xstride, + } layout_str = re.sub(r"%([0-9]*)C", r"{C:\1}", arg_format) layout_str = re.sub(r"%([-+0-9]*)T", r"{T:\1}", layout_str) layout_str = re.sub(r"%([-+0-9]*)H", r"{H:\1}", layout_str) layout_str = re.sub(r"%([-+0-9]*)R", r"{R:\1}", layout_str) layout_str = re.sub(r"%([-+0-9]*)P", r"{P:\1}", layout_str) + if xstride: + layout_str = re.sub(r"%([-+0-9]*)X", r"{X:\1}", layout_str) + else: + layout_str = re.sub(r"%([-+0-9]*)X", r"", layout_str) + layout_str = layout_str.format(**subs) return layout_str # End def format_pelayout + ############################################################################### -def print_pelayout(case, ntasks, nthreads, rootpes, pstrid, arg_format, header): +def print_pelayout(case, ntasks, nthreads, rootpes, pstrid, xstrid, arg_format, header): ############################################################################### """ Print the PE layout information for each component, using the format, @@ -144,6 +158,8 @@ def print_pelayout(case, ntasks, nthreads, rootpes, pstrid, arg_format, header): comp_classes = case.get_values("COMP_CLASSES") if header is not None: + if any(v is not None for v in xstrid.values()): + header += " XSTRIDE" print(header) # End if maxthrds = -1 @@ -155,6 +171,7 @@ def print_pelayout(case, ntasks, nthreads, rootpes, pstrid, arg_format, header): nthreads[comp], rootpes[comp], pstrid[comp], + xstrid[comp], arg_format, ) ) @@ -175,6 +192,7 @@ def print_pelayout(case, ntasks, nthreads, rootpes, pstrid, arg_format, header): # End def print_pelayout + ############################################################################### def gather_pelayout(case): ############################################################################### @@ -185,6 +203,7 @@ def gather_pelayout(case): nthreads = {} rootpes = {} pstride = {} + xstride = {} comp_classes = case.get_values("COMP_CLASSES") for comp in comp_classes: @@ -192,12 +211,19 @@ def gather_pelayout(case): nthreads[comp] = int(case.get_value("NTHRDS_" + comp)) rootpes[comp] = int(case.get_value("ROOTPE_" + comp)) pstride[comp] = int(case.get_value("PSTRID_" + comp)) + excl_stride = case.get_value("EXCL_STRIDE_" + comp) + if excl_stride: + xstride[comp] = int(case.get_value("EXCL_STRIDE_" + comp)) + else: + xstride[comp] = None + # End for - return ntasks, nthreads, rootpes, pstride + return ntasks, nthreads, rootpes, pstride, xstride # End def gather_pelayout + ############################################################################### def set_nthreads(case, nthreads): ############################################################################### @@ -210,6 +236,7 @@ def set_nthreads(case, nthreads): # End def set_nthreads + ############################################################################### def modify_ntasks(case, new_tot_tasks): ############################################################################### @@ -258,6 +285,7 @@ def modify_ntasks(case, new_tot_tasks): # End def modify_ntasks + ############################################################################### def _main_func(description): ############################################################################### @@ -274,8 +302,10 @@ def _main_func(description): if set_ntasks is not None: modify_ntasks(case, int(set_ntasks)) # End if - ntasks, nthreads, rootpes, pstrid = gather_pelayout(case) - print_pelayout(case, ntasks, nthreads, rootpes, pstrid, arg_format, header) + ntasks, nthreads, rootpes, pstrid, xstrid = gather_pelayout(case) + print_pelayout( + case, ntasks, nthreads, rootpes, pstrid, xstrid, arg_format, header + ) # End with diff --git a/CIME/Tools/standard_script_setup.py b/CIME/Tools/standard_script_setup.py index 89fe12868cc..2c38a5c4035 100644 --- a/CIME/Tools/standard_script_setup.py +++ b/CIME/Tools/standard_script_setup.py @@ -31,15 +31,15 @@ def check_minimum_python_version(major, minor, warn_only=False): + str(sys.version_info[1]) ) if warn_only: - print(msg.replace("required", "recommended") + ".") + print(msg.replace("required", "recommended") + ".", file=sys.stderr) return raise RuntimeError(msg + " - please use a newer version of Python.") -# Require users to be using >=3.6 -check_minimum_python_version(3, 6) -# Warn users if they are using <3.8 -check_minimum_python_version(3, 8, warn_only=True) +# Require users to be using >=3.9 +check_minimum_python_version(3, 9) +# Warn users if they are using <3.10 +check_minimum_python_version(3, 10, warn_only=True) real_file_dir = os.path.dirname(os.path.realpath(__file__)) cimeroot = os.path.abspath(os.path.join(real_file_dir, "..", "..")) @@ -51,4 +51,4 @@ def check_minimum_python_version(major, minor, warn_only=False): import CIME.utils CIME.utils.stop_buffering_output() -import logging, argparse +import argparse, logging diff --git a/CIME/Tools/wait_for_tests b/CIME/Tools/wait_for_tests index c166061c99b..484b393b6f4 100755 --- a/CIME/Tools/wait_for_tests +++ b/CIME/Tools/wait_for_tests @@ -87,7 +87,7 @@ OR ) parser.add_argument( - "--force-log-upload", + "--cdash-force-log-upload", action="store_true", help="Always upload logs to cdash, even if test passed", ) @@ -105,6 +105,11 @@ OR help="The name of the CDash project where results should be uploaded", ) + parser.add_argument( + "--cdash-tmproot", + help="Where to put temporary files needed to do cdash submission. Default=/tmp", + ) + parser.add_argument( "-g", "--cdash-build-group", @@ -132,9 +137,10 @@ OR args.ignore_memleak, args.cdash_build_name, args.cdash_project, + args.cdash_tmproot, args.cdash_build_group, args.timeout, - args.force_log_upload, + args.cdash_force_log_upload, args.no_run, args.update_success, ) @@ -153,6 +159,7 @@ def _main_func(description): ignore_memleak, cdash_build_name, cdash_project, + cdash_tmproot, cdash_build_group, timeout, force_log_upload, @@ -172,6 +179,7 @@ def _main_func(description): ignore_memleak=ignore_memleak, cdash_build_name=cdash_build_name, cdash_project=cdash_project, + cdash_tmproot=cdash_tmproot, cdash_build_group=cdash_build_group, timeout=timeout, force_log_upload=force_log_upload, diff --git a/CIME/XML/archive_base.py b/CIME/XML/archive_base.py index ff8c096ce9f..360424b734d 100644 --- a/CIME/XML/archive_base.py +++ b/CIME/XML/archive_base.py @@ -9,6 +9,24 @@ class ArchiveBase(GenericXML): + def get_archive_specs(self): + components_element = self.get_child("components") + + return self.get_children("comp_archive_spec", root=components_element) + + def get_rpointer_nodes(self, root): + assert root.name == "comp_archive_spec" + + return self.get_children("rpointer", root=root) + + def get_rpointers(self, root): + for node in self.get_rpointer_nodes(root): + file = self.get_child("rpointer_file", root=node).text + + content = self.get_child("rpointer_content", root=node).text + + yield file, content + def exclude_testing(self, compname): """ Checks if component should be excluded from testing. diff --git a/CIME/XML/compsets.py b/CIME/XML/compsets.py index 23eca6c825c..eef9253da0f 100644 --- a/CIME/XML/compsets.py +++ b/CIME/XML/compsets.py @@ -6,6 +6,7 @@ from CIME.XML.generic_xml import GenericXML from CIME.XML.entry_id import EntryID from CIME.XML.files import Files +from CIME.utils import CIMEError logger = logging.getLogger(__name__) @@ -25,8 +26,6 @@ def get_compset_match(self, name): is scientifically supported. science_support is returned as an array of grids for this compset """ nodes = self.get_children("compset") - alias = None - lname = None science_support = [] @@ -75,6 +74,8 @@ def get_value(self, name, attribute=None, resolved=False, subgroup=None): compsets = {} nodes = self.get_children("compset") for node in nodes: + alias = None + lname = None for child in node: logger.debug( "Here child is {} with value {}".format( @@ -85,7 +86,10 @@ def get_value(self, name, attribute=None, resolved=False, subgroup=None): alias = self.text(child) if self.name(child) == "lname": lname = self.text(child) - compsets[alias] = lname + if alias is not None and lname is not None: + compsets[alias] = lname + else: + raise CIMEError("Invalid entry in config_compsets.xml") return compsets def print_values(self, arg_help=True): diff --git a/CIME/XML/env_archive.py b/CIME/XML/env_archive.py index 3642cda40a8..e3752017aa2 100644 --- a/CIME/XML/env_archive.py +++ b/CIME/XML/env_archive.py @@ -16,22 +16,5 @@ def __init__(self, case_root=None, infile="env_archive.xml", read_only=False): schema = os.path.join(utils.get_schema_path(), "env_archive.xsd") EnvBase.__init__(self, case_root, infile, schema=schema, read_only=read_only) - def get_entries(self): - return self.get_children("comp_archive_spec") - - def get_entry_info(self, archive_entry): - compname = self.get(archive_entry, "compname") - compclass = self.get(archive_entry, "compclass") - return compname, compclass - - def get_rpointer_contents(self, archive_entry): - rpointer_items = [] - rpointer_nodes = self.get_children("rpointer", root=archive_entry) - for rpointer_node in rpointer_nodes: - file_node = self.get_child("rpointer_file", root=rpointer_node) - content_node = self.get_child("rpointer_content", root=rpointer_node) - rpointer_items.append([self.text(file_node), self.text(content_node)]) - return rpointer_items - def get_type_info(self, vid): return "char" diff --git a/CIME/XML/env_batch.py b/CIME/XML/env_batch.py index 1973e7c21dd..f37006225e6 100644 --- a/CIME/XML/env_batch.py +++ b/CIME/XML/env_batch.py @@ -549,6 +549,7 @@ def get_batch_directives(self, case, job, overrides=None, output_format="default default_queue = self.text(qnode) else: unknown_queue = False + default_queue = None for root in roots: if root is not None: diff --git a/CIME/XML/namelist_definition.py b/CIME/XML/namelist_definition.py index 7869011d573..25f7c0e0432 100644 --- a/CIME/XML/namelist_definition.py +++ b/CIME/XML/namelist_definition.py @@ -21,7 +21,7 @@ Namelist, get_fortran_name_only, ) - +from CIME.utils import CIMEError from CIME.XML.standard_module_setup import * from CIME.XML.entry_id import EntryID from CIME.XML.files import Files @@ -140,6 +140,8 @@ def get_group_name(self, node=None): group = self.get(node, "group") elif self.get_version() >= 2.0: group = self.get_element_text("group", root=node) + else: + raise CIMEError("Undefined XML file version") return group def _get_type(self, node): @@ -147,6 +149,8 @@ def _get_type(self, node): type_info = self.get(node, "type") elif self.get_version() >= 2.0: type_info = self._get_type_info(node) + else: + raise CIMEError("Undefined XML file version") return type_info def _get_valid_values(self, node): @@ -534,6 +538,8 @@ def get_input_pathname(self, name): input_pathname = self.get(node, "input_pathname") elif self.get_version() >= 2.0: input_pathname = self._get_node_element_info(node, "input_pathname") + else: + raise CIMEError("Invalid XML file version") return input_pathname # pylint: disable=arguments-differ diff --git a/CIME/XML/pes.py b/CIME/XML/pes.py index 3254751c794..666382c0a38 100644 --- a/CIME/XML/pes.py +++ b/CIME/XML/pes.py @@ -1,6 +1,7 @@ """ Interface to the config_pes.xml file. This class inherits from GenericXML.py """ + from CIME.XML.standard_module_setup import * from CIME.XML.generic_xml import GenericXML from CIME.XML.files import Files @@ -25,6 +26,7 @@ def find_pes_layout(self, grid, compset, machine, pesize_opts="M", mpilib=None): opes_nthrds = {} opes_rootpe = {} opes_pstrid = {} + opes_excl_stride = {} oother_settings = {} other_settings = {} append = {} @@ -41,6 +43,7 @@ def find_pes_layout(self, grid, compset, machine, pesize_opts="M", mpilib=None): opes_nthrds, opes_rootpe, opes_pstrid, + opes_excl_stride, oother_settings, append, ocomments, @@ -60,6 +63,7 @@ def find_pes_layout(self, grid, compset, machine, pesize_opts="M", mpilib=None): pes_nthrds, pes_rootpe, pes_pstrid, + pes_excl_stride, other_settings, os_append, comments, @@ -68,6 +72,7 @@ def find_pes_layout(self, grid, compset, machine, pesize_opts="M", mpilib=None): pes_nthrds.update(opes_nthrds) pes_rootpe.update(opes_rootpe) pes_pstrid.update(opes_pstrid) + pes_excl_stride.update(opes_excl_stride) other_settings.update(oother_settings) os_append.update(append) if ocomments is not None: @@ -80,6 +85,8 @@ def find_pes_layout(self, grid, compset, machine, pesize_opts="M", mpilib=None): pes_rootpe[i] = 0 for i in iter(pes_pstrid): pes_pstrid[i] = 0 + for i in iter(pes_excl_stride): + pes_pstrid[i] = 0 logger.info("Pes setting: grid is {} ".format(grid)) logger.info("Pes setting: compset is {} ".format(compset)) @@ -87,6 +94,7 @@ def find_pes_layout(self, grid, compset, machine, pesize_opts="M", mpilib=None): logger.info("Pes setting: threads is {} ".format(pes_nthrds)) logger.info("Pes setting: rootpe is {} ".format(pes_rootpe)) logger.info("Pes setting: pstrid is {} ".format(pes_pstrid)) + logger.info("Pes setting: excl_stride is {} ".format(pes_excl_stride)) logger.info("Pes other settings: {}".format(other_settings)) logger.info("Pes other settings append: {}".format(os_append)) if comments is not None: @@ -97,6 +105,7 @@ def find_pes_layout(self, grid, compset, machine, pesize_opts="M", mpilib=None): pes_nthrds, pes_rootpe, pes_pstrid, + pes_excl_stride, other_settings, os_append, comments, @@ -110,7 +119,16 @@ def _find_matches( compset_choice = None pesize_choice = None max_points = -1 - pes_ntasks, pes_nthrds, pes_rootpe, pes_pstrid, other_settings, append = ( + ( + pes_ntasks, + pes_nthrds, + pes_rootpe, + pes_pstrid, + pes_excl_stride, + other_settings, + append, + ) = ( + {}, {}, {}, {}, @@ -173,6 +191,11 @@ def _find_matches( pes_pstrid[ self.name(child).upper() ] = int(self.text(child)) + elif "excl_stride" in vid: + for child in self.get_children(root=node): + pes_excl_stride[ + self.name(child).upper() + ] = int(self.text(child)) # if the value is already upper case its something else we are trying to set else: other_settings[vid] = self.text(node) @@ -231,6 +254,11 @@ def _find_matches( elif "pstrid" in vid: for child in self.get_children(root=node): pes_pstrid[self.name(child).upper()] = int(self.text(child)) + elif "excl_stride" in vid: + for child in self.get_children(root=node): + pes_excl_stride[self.name(child).upper()] = int( + self.text(child) + ) # if the value is already upper case its something else we are trying to set elif vid == self.name(node): text = self.text(node).strip() @@ -250,6 +278,7 @@ def _find_matches( pes_nthrds, pes_rootpe, pes_pstrid, + pes_excl_stride, other_settings, append, comment, diff --git a/CIME/XML/tests.py b/CIME/XML/tests.py index 4a9eefc0fc4..e760d86604f 100644 --- a/CIME/XML/tests.py +++ b/CIME/XML/tests.py @@ -5,7 +5,7 @@ from CIME.XML.generic_xml import GenericXML from CIME.XML.files import Files -from CIME.utils import find_system_test +from CIME.utils import find_system_test, CIMEError from CIME.SystemTests.system_tests_compare_two import SystemTestsCompareTwo from CIME.SystemTests.system_tests_compare_n import SystemTestsCompareN @@ -22,13 +22,20 @@ def __init__(self, infile=None, files=None): files = Files() infile = files.get_value("CONFIG_TESTS_FILE") GenericXML.__init__(self, infile) - # append any component specific config_tests.xml files + + # Append any component-specific config_tests.xml files. We take care to only add a + # given file once, since adding a given file multiple times creates a "multiple + # matches" error. (This can happen if multiple CONFIG_TESTS_FILEs resolve to the + # same path.) + files_added = set() for comp in files.get_components("CONFIG_TESTS_FILE"): if comp is None: continue infile = files.get_value("CONFIG_TESTS_FILE", attribute={"component": comp}) - if os.path.isfile(infile): + infile_abspath = os.path.abspath(infile) + if os.path.isfile(infile) and infile_abspath not in files_added: self.read(infile) + files_added.add(infile_abspath) def support_single_exe(self, case): """Checks if case supports --single-exe. @@ -52,8 +59,7 @@ def support_single_exe(self, case): if not valid: case_base_id = case.get_value("CASEBASEID") - - raise Exception( + raise CIMEError( f"{case_base_id} does not support the '--single-exe' option as it requires separate builds" ) diff --git a/CIME/XML/workflow.py b/CIME/XML/workflow.py index 4824e03b666..cdb2d27b403 100644 --- a/CIME/XML/workflow.py +++ b/CIME/XML/workflow.py @@ -80,6 +80,6 @@ def get_workflow_jobs(self, machine, workflowid="default"): else: jdict[self.name(child)] = self.text(child) - jobs.append((name, jdict)) + jobs.append((name, jdict)) return jobs diff --git a/CIME/bless_test_results.py b/CIME/bless_test_results.py index e7e601b682e..3028cfa4390 100644 --- a/CIME/bless_test_results.py +++ b/CIME/bless_test_results.py @@ -6,6 +6,7 @@ EnvironmentContext, parse_test_name, match_any, + CIMEError, ) from CIME.config import Config from CIME.test_status import * @@ -22,6 +23,48 @@ logger = logging.getLogger(__name__) +class BlessError(Exception): + def __init__(self, test_name, reasons): + if isinstance(reasons, str): + reasons = list(reasons) + + self._test_name = test_name + self._index = 0 + self._reasons = reasons + + super().__init__() + + @property + def test_name(self): + return self._test_name + + def __iter__(self): + self._index = 0 + + return self + + def __next__(self): + if self._index > len(self._reasons) - 1: + raise StopIteration() + + item = self._reasons[self._index] + + self._index += 1 + + return item + + def __len__(self): + return len(self._reasons) + + def __str__(self): + return f"There were {len(self)} bless errors" + + def __repr__(self): + reason_str = ",".join(self._reasons) if len(self) > 0 else "" + + return f"BlessError(reasons=[{reason_str}])" + + def _bless_throughput( case, test_name, @@ -367,108 +410,28 @@ def bless_test_results( if not force: time.sleep(2) - with Case(test_dir) as case: - # Resolve baseline_name and baseline_root - if baseline_name is None: - baseline_name_resolved = case.get_value("BASELINE_NAME_CMP") - if not baseline_name_resolved: - cime_root = CIME.utils.get_cime_root() - baseline_name_resolved = CIME.utils.get_current_branch( - repo=cime_root - ) - else: - baseline_name_resolved = baseline_name - - if baseline_root is None: - baseline_root_resolved = case.get_value("BASELINE_ROOT") - else: - baseline_root_resolved = baseline_root - - if baseline_name_resolved is None: - broken_blesses.append( - (test_name, "Could not determine baseline name") - ) - continue - - if baseline_root_resolved is None: - broken_blesses.append( - (test_name, "Could not determine baseline root") - ) - continue - - # Bless namelists - if nl_bless: - success, reason = bless_namelists( - test_name, - report_only, - force, - pes_file, - baseline_name_resolved, - baseline_root_resolved, - new_test_root=new_test_root, - new_test_id=new_test_id, - ) - if not success: - broken_blesses.append((test_name, reason)) - - # Bless hist files - if hist_bless: - if "HOMME" in test_name: - success = False - reason = "HOMME tests cannot be blessed with bless_for_tests" - else: - success, reason = bless_history( - test_name, - case, - baseline_name_resolved, - baseline_root_resolved, - report_only, - force, - ) - - if not success: - broken_blesses.append((test_name, reason)) - - if tput_bless: - success, reason = _bless_throughput( - case, - test_name, - baseline_root_resolved, - baseline_name_resolved, - report_only, - force, - ) - - if not success: - broken_blesses.append((test_name, reason)) - - if mem_bless: - success, reason = _bless_memory( - case, - test_name, - baseline_root_resolved, - baseline_name_resolved, - report_only, - force, - ) - - if not success: - broken_blesses.append((test_name, reason)) - - if lock_baselines: - baseline_full_dir = os.path.join( - baseline_root_resolved, - baseline_name_resolved, - case.get_value("CASEBASEID"), - ) - stat, out, _ = run_cmd( - f"chmod -R g-w {baseline_full_dir}", combine_output=True - ) - if stat != 0: - msg = ( - f"Failed to lock baselines for {baseline_full_dir}:\n{out}" - ) - logger.warning(msg) + try: + _bless_test( + test_name, + test_dir, + baseline_name, + baseline_root, + nl_bless, + hist_bless, + tput_bless, + mem_bless, + lock_baselines, + pes_file, + new_test_root, + new_test_id, + report_only, + force, + ) + except BlessError as e: + for reason in e: + broken_blesses.append((e.test_name, reason)) + except CIMEError as e: + broken_blesses.append((test_name, str(e))) # Emit a warning if items in bless_tests did not match anything if bless_tests: @@ -494,6 +457,121 @@ def bless_test_results( return success +def _bless_test( + test_name, + test_dir, + baseline_name, + baseline_root, + nl_bless, + hist_bless, + tput_bless, + mem_bless, + lock_baselines, + pes_file, + new_test_root, + new_test_id, + report_only, + force, +): + with Case(test_dir) as case: + # Resolve baseline_name and baseline_root + if baseline_name is None: + baseline_name_resolved = case.get_value("BASELINE_NAME_CMP") + if not baseline_name_resolved: + cime_root = CIME.utils.get_cime_root() + baseline_name_resolved = CIME.utils.get_current_branch(repo=cime_root) + else: + baseline_name_resolved = baseline_name + + if baseline_root is None: + baseline_root_resolved = case.get_value("BASELINE_ROOT") + else: + baseline_root_resolved = baseline_root + + if baseline_name_resolved is None: + raise BlessError(test_name, "Could not determine baseline name") + + if baseline_root_resolved is None: + raise BlessError(test_name, "Could not determine baseline root") + + bless_errors = [] + + # Bless namelists + if nl_bless: + success, reason = bless_namelists( + test_name, + report_only, + force, + pes_file, + baseline_name_resolved, + baseline_root_resolved, + new_test_root=new_test_root, + new_test_id=new_test_id, + ) + if not success: + bless_errors.append(reason) + + # Bless hist files + if hist_bless: + if "HOMME" in test_name: + success = False + reason = "HOMME tests cannot be blessed with bless_for_tests" + else: + success, reason = bless_history( + test_name, + case, + baseline_name_resolved, + baseline_root_resolved, + report_only, + force, + ) + + if not success: + bless_errors.append(reason) + + if tput_bless: + success, reason = _bless_throughput( + case, + test_name, + baseline_root_resolved, + baseline_name_resolved, + report_only, + force, + ) + + if not success: + bless_errors.append(reason) + + if mem_bless: + success, reason = _bless_memory( + case, + test_name, + baseline_root_resolved, + baseline_name_resolved, + report_only, + force, + ) + + if not success: + bless_errors.append(reason) + + if lock_baselines: + baseline_full_dir = os.path.join( + baseline_root_resolved, + baseline_name_resolved, + case.get_value("CASEBASEID"), + ) + stat, out, _ = run_cmd( + f"chmod -R g-w {baseline_full_dir}", combine_output=True + ) + if stat != 0: + msg = f"Failed to lock baselines for {baseline_full_dir}:\n{out}" + logger.warning(msg) + + if len(bless_errors) > 0: + raise BlessError(test_name, bless_errors) + + def is_hist_bless_needed( test_name, ts, broken_blesses, overall_result, no_skip_pass, phase ): diff --git a/CIME/build.py b/CIME/build.py index ed9ca2fd3ec..af1709bb59c 100644 --- a/CIME/build.py +++ b/CIME/build.py @@ -245,12 +245,16 @@ def get_standard_cmake_args(case, sharedpath): cmake_args += " -Dcompile_threaded={} ".format( stringify_bool(case.get_build_threaded()) ) - # check settings for GPU - gpu_type = case.get_value("GPU_TYPE") - openacc_gpu_offload = case.get_value("OPENACC_GPU_OFFLOAD") - openmp_gpu_offload = case.get_value("OPENMP_GPU_OFFLOAD") - kokkos_gpu_offload = case.get_value("KOKKOS_GPU_OFFLOAD") - cmake_args += f" -DGPU_TYPE={gpu_type} -DOPENACC_GPU_OFFLOAD={openacc_gpu_offload} -DOPENMP_GPU_OFFLOAD={openmp_gpu_offload} -DKOKKOS_GPU_OFFLOAD={kokkos_gpu_offload} " + # check for optional settings for GPU + for item in [ + "GPU_TYPE", + "OPENACC_GPU_OFFLOAD", + "OPENMP_GPU_OFFLOAD", + "KOKKOS_GPU_OFFLOAD", + ]: + item_value = case.get_value(item) + if item_value: + cmake_args += f" -D{item}={item_value}" ocn_model = case.get_value("COMP_OCN") atm_dycore = case.get_value("CAM_DYCORE") diff --git a/CIME/case/case.py b/CIME/case/case.py index ae7fae9b94e..bd6db1e503c 100644 --- a/CIME/case/case.py +++ b/CIME/case/case.py @@ -1204,6 +1204,7 @@ def _setup_mach_pes(self, pecount, multi_driver, ninst, machine_name, mpilib): pes_nthrds = {} pes_rootpe = {} pes_pstrid = {} + pes_excl_stride = {} other = {} append = {} comment = None @@ -1233,6 +1234,7 @@ def _setup_mach_pes(self, pecount, multi_driver, ninst, machine_name, mpilib): pes_nthrds, pes_rootpe, pes_pstrid, + pes_excl_stride, other, append, comment, @@ -1287,17 +1289,24 @@ def _setup_mach_pes(self, pecount, multi_driver, ninst, machine_name, mpilib): nthrds_str = "NTHRDS_{}".format(comp_class) rootpe_str = "ROOTPE_{}".format(comp_class) pstrid_str = "PSTRID_{}".format(comp_class) + excl_stride_str = "EXCL_STRIDE_{}".format(comp_class) ntasks = pes_ntasks[ntasks_str] if ntasks_str in pes_ntasks else 1 nthrds = pes_nthrds[nthrds_str] if nthrds_str in pes_nthrds else 1 rootpe = pes_rootpe[rootpe_str] if rootpe_str in pes_rootpe else 0 pstrid = pes_pstrid[pstrid_str] if pstrid_str in pes_pstrid else 1 + excl_stride = ( + pes_excl_stride[excl_stride_str] + if excl_stride_str in pes_excl_stride + else 0 + ) totaltasks.append((ntasks + rootpe) * nthrds) mach_pes_obj.set_value(ntasks_str, ntasks) mach_pes_obj.set_value(nthrds_str, nthrds) mach_pes_obj.set_value(rootpe_str, rootpe) mach_pes_obj.set_value(pstrid_str, pstrid) + mach_pes_obj.set_value(excl_stride_str, excl_stride) # Make sure that every component has been accounted for # set, nthrds and ntasks to 1 otherwise. Also set the ninst values here. diff --git a/CIME/case/case_cmpgen_namelists.py b/CIME/case/case_cmpgen_namelists.py index 10ded368147..6a2579ada35 100644 --- a/CIME/case/case_cmpgen_namelists.py +++ b/CIME/case/case_cmpgen_namelists.py @@ -95,11 +95,14 @@ def _do_full_nl_gen_impl(case, test, generate_name, baseline_root=None): shutil.copytree(casedoc_dir, baseline_casedocs) - for item in glob.glob(os.path.join(test_dir, "user_nl*")): - preexisting_baseline = os.path.join(baseline_dir, os.path.basename(item)) - if os.path.exists(preexisting_baseline): - os.remove(preexisting_baseline) + # Note: If it is ever the case that nml cmp/gen affects more files than + # just user_nl* and CaseDocs, this will break assumptions all across CIME. + + # Remove any previous user_nl files + for item in glob.glob(os.path.join(baseline_dir, "user_nl*")): + os.remove(item) + for item in glob.glob(os.path.join(test_dir, "user_nl*")): safe_copy(item, baseline_dir, preserve_meta=False) diff --git a/CIME/case/case_run.py b/CIME/case/case_run.py index 3e7c8baa37d..b9912589e5f 100644 --- a/CIME/case/case_run.py +++ b/CIME/case/case_run.py @@ -1,6 +1,7 @@ """ case_run is a member of Class Case '""" + from CIME.XML.standard_module_setup import * from CIME.config import Config from CIME.utils import gzip_existing_file, new_lid @@ -304,13 +305,10 @@ def _post_run_check(case, lid): rundir = case.get_value("RUNDIR") driver = case.get_value("COMP_INTERFACE") - - comp_standalone, model = is_comp_standalone(case) - + comp_standalone = False if driver == "nuopc": - if comp_standalone: - file_prefix = model - else: + comp_standalone, file_prefix = is_comp_standalone(case) + if not comp_standalone: file_prefix = "med" else: file_prefix = "cpl" @@ -318,6 +316,7 @@ def _post_run_check(case, lid): cpl_ninst = 1 if case.get_value("MULTI_DRIVER"): cpl_ninst = case.get_value("NINST_MAX") + cpl_logs = [] if cpl_ninst > 1: @@ -335,11 +334,14 @@ def _post_run_check(case, lid): cpl_logs.append(os.path.join(rundir, "med.log." + lid)) cpl_logfile = cpl_logs[0] # find the last model.log and cpl.log - model_logfile = os.path.join(rundir, model + ".log." + lid) - if not os.path.isfile(model_logfile): - expect(False, "Model did not complete, no {} log file ".format(model_logfile)) - elif os.stat(model_logfile).st_size == 0: - expect(False, "Run FAILED") + if comp_standalone: + model_logfile = os.path.join(rundir, file_prefix + ".log." + lid) + if not os.path.isfile(model_logfile): + expect( + False, "Model did not complete, no {} log file ".format(model_logfile) + ) + elif os.stat(model_logfile).st_size == 0: + expect(False, "Run FAILED") else: count_ok = 0 for cpl_logfile in cpl_logs: diff --git a/CIME/case/case_setup.py b/CIME/case/case_setup.py index 98671ba24d2..38d6a4b0336 100644 --- a/CIME/case/case_setup.py +++ b/CIME/case/case_setup.py @@ -20,6 +20,7 @@ copy_local_macros_to_dir, batch_jobid, run_cmd_no_fail, + CIMEError, ) from CIME.status import run_and_log_case_status, append_case_status from CIME.test_status import * @@ -151,13 +152,15 @@ def _create_macros_cmake( # This impl is coupled to contents of Macros.cmake os_ = mach_obj.get_value("OS") mach = mach_obj.get_machine_name() + deprecated = "{}_{}.cmake".format(compiler, mach) macros = [ "universal.cmake", os_ + ".cmake", compiler + ".cmake", "{}_{}.cmake".format(compiler, os), mach + ".cmake", - "{}_{}.cmake".format(compiler, mach), + "{}_{}.cmake".format(mach, compiler), + deprecated, "CMakeLists.txt", ] for macro in macros: @@ -165,10 +168,19 @@ def _create_macros_cmake( mach_repo_macro = os.path.join(cmake_macros_dir, "..", mach, macro) case_macro = os.path.join(case_cmake_path, macro) if not os.path.exists(case_macro): + copied = False if os.path.exists(mach_repo_macro): safe_copy(mach_repo_macro, case_cmake_path) + copied = True elif os.path.exists(repo_macro): safe_copy(repo_macro, case_cmake_path) + copied = True + + if copied and macro == deprecated: + logger.warning( + "\nWARNING: Macros of the form COMPILER_MACHINE.cmake are deprecated " + "and should be replaced with the form MACHINE_COMPILER.cmake\n" + ) copy_depends_files(mach, mach_obj.machines_dir, caseroot, compiler) @@ -309,15 +321,17 @@ def _case_setup_impl( comp_interface = case.get_value("COMP_INTERFACE") if comp_interface == "nuopc": ninst = case.get_value("NINST") + else: + ninst = 1 multi_driver = case.get_value("MULTI_DRIVER") - for comp in models: ntasks = case.get_value("NTASKS_{}".format(comp)) if comp == "CPL": continue if comp_interface != "nuopc": ninst = case.get_value("NINST_{}".format(comp)) + if multi_driver: if comp_interface != "nuopc": expect( @@ -407,11 +421,11 @@ def _case_setup_impl( ngpus_per_node = case.get_value("NGPUS_PER_NODE") if gpu_type and str(gpu_type).lower() != "none": if max_gpus_per_node <= 0: - raise RuntimeError( + raise CIMEError( f"MAX_GPUS_PER_NODE must be larger than 0 for machine={mach} and compiler={compiler} in order to configure a GPU run" ) if not gpu_offload: - raise RuntimeError( + raise CIMEError( "GPU_TYPE is defined but none of the GPU OFFLOAD options are enabled" ) case.gpu_enabled = True @@ -423,11 +437,11 @@ def _case_setup_impl( else max_gpus_per_node, ) elif gpu_offload: - raise RuntimeError( + raise CIMEError( "GPU_TYPE is not defined but at least one GPU OFFLOAD option is enabled" ) elif ngpus_per_node and ngpus_per_node != 0: - raise RuntimeError( + raise CIMEError( f"ngpus_per_node is expected to be 0 for a pure CPU run ; {ngpus_per_node} is provided instead ;" ) @@ -484,8 +498,8 @@ def _case_setup_impl( ): logger.info("Running cam.case_setup.py") run_cmd_no_fail( - "python {cam}/cime_config/cam.case_setup.py {cam} {case}".format( - cam=camroot, case=caseroot + "python {cam}/cime_config/cam.case_setup.py {cam} {case} {non_local}".format( + cam=camroot, case=caseroot, non_local=str(non_local) ) ) diff --git a/CIME/case/case_st_archive.py b/CIME/case/case_st_archive.py index 825ae95a676..1b7fac4d25b 100644 --- a/CIME/case/case_st_archive.py +++ b/CIME/case/case_st_archive.py @@ -155,7 +155,6 @@ def _get_component_archive_entries(components, archive): yield (archive_entry, compname, compclass) -############################################################################### def _archive_rpointer_files( casename, ninst_strings, @@ -167,93 +166,110 @@ def _archive_rpointer_files( datename, datename_is_last, ): - ############################################################################### - + """Archive rpointer files. + + For a given restart point, an rpointer file is archived. If the rpointer file + for this point exists it will be copied/moved to the archive directory otherwise + it will be created using the `rpointer_file` and `rpointer_content` elements. + + The following variables can be used in `rpointer_file` and `rpointer_content` + and will be substitued with the appropriate values. + + - $CASE: The name of the case. + - $DATENAME: The date of the restart file. + - $MPAS_DATENAME: The date of the restart file in an MPAS specific format. + - $NINST_STRING: The identifier if running multiple instances. + + Args: + casename (str): Name of the case. + ninst_strings (list): List of ninst identifiers. + rundir (str): Path to the run directory. + save_interim_restart_files (bool): Whether to save the intermediate restart files. + archive (CIME.XML.env_archive.EnvArchive): Environment archive object. + archive_entry (CIME.XML.generic_xml.Element): Rpointer XML node. + archive_restdir (str): Path to the directory to write rpointer files. + datename (CIME.date.date): Date object with the rpointer date. + datename_is_last (bool): Whether this rpointer is the latest. + + Raises: + CIMEError: If `rpointer_file` template cannot be resolved. + """ # parse env_archive.xml to determine the rpointer files # and contents for the given archive_entry tag # loop through the possible rpointer files and contents - rpointer_nodes = archive.get_children("rpointer", root=archive_entry) - for rpointer in rpointer_nodes: - file_node = archive.get_child("rpointer_file", root=rpointer) - temp_rpointer_file = archive.text(file_node) - content_node = archive.get_child("rpointer_content", root=rpointer) - temp_rpointer_content = archive.text(content_node) - rpointer_file = temp_rpointer_file.replace("$NINST_STRING", "*") + for rpointer_file, rpointer_content in archive.get_rpointers(archive_entry): if rpointer_file == "unset": continue - if "$DATENAME" in rpointer_file: - rpointer_file = rpointer_file.replace("$DATENAME", _datetime_str(datename)) + + replacements = { + "$CASE": casename, + "$DATENAME": _datetime_str(datename), + "$MPAS_DATENAME": _datetime_str_mpas(datename), + } + + for key, value in replacements.items(): + rpointer_file = rpointer_file.replace(key, value) + rpointer_content = rpointer_content.replace(key, value) + + rpointer_file_glob = rpointer_file.replace("$NINST_STRING", "*") expect( - not "$" in rpointer_file, - "Unrecognized expression in name {}".format(rpointer_file), + not "$" in rpointer_file_glob, + "Unrecognized expression in name {}".format(rpointer_file_glob), ) - rpointers = glob.glob(rundir + "/" + rpointer_file) + rpointers = glob.glob(rundir + "/" + rpointer_file_glob) if datename_is_last: for rpfile in rpointers: safe_copy( rpfile, os.path.join(archive_restdir, os.path.basename(rpfile)) ) - else: + elif save_interim_restart_files: # Generate rpointer file(s) for interim restarts for the one datename and each # possible value of ninst_strings - if save_interim_restart_files: - # If timestamped rpointers exist use them - if rpointers: - for rpfile in rpointers: - logger.info("moving interim rpointer_file {}".format(rpfile)) - shutil.move( - rpfile, - os.path.join(archive_restdir, os.path.basename(rpfile)), - ) - else: - # put in a temporary setting for ninst_strings if they are empty - # in order to have just one loop over ninst_strings below - if ninst_strings: - rpointer_content = temp_rpointer_content.replace( - "$NINST_STRING", ninst_strings[0] - ) - else: - rpointer_content = temp_rpointer_content.replace( - "$NINST_STRING", "" - ) - rpointer_content = rpointer_content.replace( - "$DATENAME", _datetime_str(datename) + # If timestamped rpointers exist use them + # If only one rpointer file, leave in run directorya and create in archive + if rpointers and len(rpointers) > 1: + for rpfile in rpointers: + logger.info("moving interim rpointer_file {}".format(rpfile)) + shutil.move( + rpfile, + os.path.join(archive_restdir, os.path.basename(rpfile)), ) - if rpointer_content != "unset": - if not ninst_strings: - ninst_strings = ["empty"] - - for ninst_string in ninst_strings: - rpointer_file = temp_rpointer_file - rpointer_content = temp_rpointer_content - if ninst_string == "empty": - ninst_string = "" - for key, value in [ - ("$CASE", casename), - ("$DATENAME", _datetime_str(datename)), - ("$MPAS_DATENAME", _datetime_str_mpas(datename)), - ("$NINST_STRING", ninst_string), - ]: - rpointer_file = rpointer_file.replace(key, value) - rpointer_content = rpointer_content.replace(key, value) - - # write out the respective files with the correct contents - rpointer_file = os.path.join(archive_restdir, rpointer_file) - logger.info( - "writing rpointer_file {}".format(rpointer_file) - ) - f = open(rpointer_file, "w") - for output in rpointer_content.split(","): - f.write("{} \n".format(output)) - f.close() - else: - logger.info( - "rpointer_content unset, not creating rpointer file {}".format( - rpointer_file - ) + else: + if rpointer_content == "unset": + logger.info( + "rpointer_content unset, not creating rpointer file {}".format( + rpointer_file ) + ) + + return + + # need to loop atleast once + if not ninst_strings: + ninst_strings = [ + "", + ] + + for ninst_string in ninst_strings: + ninst_rpointer_file = rpointer_file.replace( + "$NINST_STRING", ninst_string + ) + ninst_rpointer_content = rpointer_content.replace( + "$NINST_STRING", ninst_string + ) + + # write out the respective files with the correct contents + ninst_rpointer_path = os.path.join( + archive_restdir, ninst_rpointer_file + ) + + logger.info("writing rpointer_file {}".format(ninst_rpointer_path)) + + with open(ninst_rpointer_path, "w") as f: + for output in ninst_rpointer_content.split(","): + f.write("{} \n".format(output)) ############################################################################### @@ -284,6 +300,11 @@ def _archive_log_files(dout_s_root, rundir, archive_incomplete, archive_file_fn) ) ) archive_file_fn(srcfile, destfile) + # Finally copy the CASEROOT file into the archive directory + caseroot = os.path.join(rundir, "CASEROOT") + logdir_caseroot = os.path.join(archive_logdir, "CASEROOT") + if os.path.exists(caseroot) and not os.path.exists(logdir_caseroot): + safe_copy(os.path.join(rundir, "CASEROOT"), archive_logdir) ############################################################################### @@ -1141,6 +1162,14 @@ def case_st_archive( verbose=True, ) else: + # this is the only way you can jump from a partition with mem specs allowed + # to the one where are they prohibited by whoever set up slurm configs on HPC + # TODO: add betzy + if self.get_value("MACH") in ["olivia","betzy"]: + logger.info("redefine SLURM_MEM_PER_NODE environment variable") + os.unsetenv("SLURM_MEM_PER_NODE") + # this slurm var is in MiB we set MEM_PER_TASK and MAX_MEM_PER_NODE in GiB + os.environ["SLURM_MEM_PER_NODE"]="{}".format(self.get_value("MAX_MEM_PER_NODE")*1024) self.submit(resubmit=True) return True @@ -1181,7 +1210,9 @@ def test_st_archive(self, testdir="st_archive_test"): ) with open(fname, "w") as fd: fd.write(disposition + "\n") - + caseroot = self.get_value("CASEROOT") + with open(os.path.join(testdir, "CASEROOT"), "w") as f: + f.write(caseroot) logger.info("testing components: {} ".format(list(set(components)))) _archive_process( self, @@ -1331,6 +1362,8 @@ def _check_disposition(testdir): copyfilelist = [] for root, _, files in os.walk(testdir): for _file in files: + if "CASEROOT" in _file: + continue with open(os.path.join(root, _file), "r") as fd: disposition = fd.readline() logger.info( diff --git a/CIME/case/case_submit.py b/CIME/case/case_submit.py index 21e03c05d02..9251e4ab092 100644 --- a/CIME/case/case_submit.py +++ b/CIME/case/case_submit.py @@ -355,6 +355,9 @@ def check_case(self, skip_pnl=False, chksum=False): elif ncpl_base_period == "decade": coupling_secs = 315360000 / maxncpl timestep = 315360000 / minncpl + else: + raise CIMEError("ncpl_base_period handling error") + stop_option = self.get_value("STOP_OPTION") stop_n = self.get_value("STOP_N") if stop_option == "nsteps": diff --git a/CIME/case/check_input_data.py b/CIME/case/check_input_data.py index 0aa0a3acdf0..cc67e1c6aca 100644 --- a/CIME/case/check_input_data.py +++ b/CIME/case/check_input_data.py @@ -32,6 +32,7 @@ def _download_checksum_file(rundir): protocol, user, passwd ) ) + server = None if protocol == "svn": server = CIME.Servers.SVN(address, user, passwd) elif protocol == "gftp": @@ -42,6 +43,7 @@ def _download_checksum_file(rundir): server = CIME.Servers.WGET.wget_login(address, user, passwd) else: expect(False, "Unsupported inputdata protocol: {}".format(protocol)) + if not server: continue @@ -190,7 +192,8 @@ def _check_all_input_data_impl( else: if chksum: chksum_found = _download_checksum_file(self.get_value("RUNDIR")) - + else: + chksum_found = False clm_usrdat_name = self.get_value("CLM_USRDAT_NAME") if clm_usrdat_name and clm_usrdat_name == "UNSET": clm_usrdat_name = None @@ -283,7 +286,7 @@ def stage_refcase(self, input_data_root=None, data_list_dir=None): get_refcase = self.get_value("GET_REFCASE") run_type = self.get_value("RUN_TYPE") continue_run = self.get_value("CONTINUE_RUN") - + drv_restart_pointer = self.get_value("DRV_RESTART_POINTER") # We do not fully populate the inputdata directory on every # machine and do not expect every user to download the 3TB+ of # data in our inputdata repository. This code checks for the @@ -335,17 +338,23 @@ def stage_refcase(self, input_data_root=None, data_list_dir=None): logger.debug("Creating run directory: {}".format(rundir)) os.makedirs(rundir) rpointerfile = None + # copy the refcases' rpointer files to the run directory for rpointerfile in glob.iglob(os.path.join("{}", "*rpointer*").format(refdir)): logger.info("Copy rpointer {}".format(rpointerfile)) safe_copy(rpointerfile, rundir) - os.chmod(os.path.join(rundir, os.path.basename(rpointerfile)), 0o644) + pfile = os.path.basename(rpointerfile) + os.chmod(os.path.join(rundir, pfile), 0o644) + if "cpl" in pfile and drv_restart_pointer != pfile: + self.set_value("DRV_RESTART_POINTER", pfile) + expect( rpointerfile, "Reference case directory {} does not contain any rpointer files".format( refdir ), ) + # link everything else for rcfile in glob.iglob(os.path.join(refdir, "*")): @@ -403,6 +412,7 @@ def _check_input_data_impl( ) no_files_missing = True + server = None if download: if protocol not in vars(CIME.Servers): logger.info("Client protocol {} not enabled".format(protocol)) diff --git a/CIME/compare_test_results.py b/CIME/compare_test_results.py index f14263adf4b..35274cc74ba 100644 --- a/CIME/compare_test_results.py +++ b/CIME/compare_test_results.py @@ -151,7 +151,6 @@ def compare_test_results( logfile_name, test_dir, ) - if nl_do_compare or do_compare: if nl_do_compare: nl_success = compare_namelists( @@ -182,8 +181,7 @@ def compare_test_results( brief_result += "{} {} {} {}\n".format( nl_compare_result, test_name, NAMELIST_PHASE, nl_compare_comment ) - - if not namelists_only: + if not namelists_only and not build_only: brief_result += "{} {} {}".format( compare_result, test_name, BASELINE_PHASE ) diff --git a/CIME/data/config/cesm/config_files.xml b/CIME/data/config/cesm/config_files.xml index 3a728b7af84..07f087549b5 100644 --- a/CIME/data/config/cesm/config_files.xml +++ b/CIME/data/config/cesm/config_files.xml @@ -112,6 +112,7 @@ $CIMEROOT/CIME/data/config/config_tests.xml + $SRCROOT/cime_config/config_tests.xml $COMP_ROOT_DIR_LND/cime_config/config_tests.xml $COMP_ROOT_DIR_LND/cime_config/config_tests.xml $COMP_ROOT_DIR_ATM/cime_config/config_tests.xml @@ -133,7 +134,7 @@ $SRCROOT/components/cdeps/datm $CIMEROOT/CIME/non_py/src/components/stub_comps_$COMP_INTERFACE/satm - $CIMEROOT/CIME/non_py/src/components/xcpl_comps_$COMP_INTERFACE/xatm + $SRCROOT/components/cmeps/med_test_comps/xatm $SRCROOT/components/cam/ $SRCROOT/components/fv3/ @@ -168,7 +169,7 @@ $SRCROOT/components/blom/ $SRCROOT/components/cdeps/docn $CIMEROOT/CIME/non_py/src/components/stub_comps_$COMP_INTERFACE/socn - $CIMEROOT/CIME/non_py/src/components/xcpl_comps_$COMP_INTERFACE/xocn + $SRCROOT/components/cmeps/med_test_comps/xocn case_comps env_case.xml @@ -183,7 +184,7 @@ $SRCROOT/components/ww3/ $SRCROOT/components/cdeps/dwav $CIMEROOT/CIME/non_py/src/components/stub_comps_$COMP_INTERFACE/swav - $CIMEROOT/CIME/non_py/src/components/xcpl_comps_$COMP_INTERFACE/xwav + $SRCROOT/components/cmeps/med_test_comps/xwav case_comps env_case.xml @@ -198,7 +199,7 @@ $SRCROOT/components/cism/ $SRCROOT/components/cdeps/dglc $CIMEROOT/CIME/non_py/src/components/stub_comps_$COMP_INTERFACE/sglc - $CIMEROOT/CIME/non_py/src/components/xcpl_comps_$COMP_INTERFACE/xglc + $SRCROOT/components/cmeps/med_test_comps/xglc case_comps env_case.xml @@ -215,7 +216,7 @@ $SRCROOT/components/mpas-seaice/ $SRCROOT/components/cdeps/dice $CIMEROOT/CIME/non_py/src/components/stub_comps_$COMP_INTERFACE/sice - $CIMEROOT/CIME/non_py/src/components/xcpl_comps_$COMP_INTERFACE/xice + $SRCROOT/components/cmeps/med_test_comps/xice case_comps env_case.xml @@ -232,7 +233,7 @@ $SRCROOT/components/mizuRoute/ $SRCROOT/components/cdeps/drof $CIMEROOT/CIME/non_py/src/components/stub_comps_$COMP_INTERFACE/srof - $CIMEROOT/CIME/non_py/src/components/xcpl_comps_$COMP_INTERFACE/xrof + $SRCROOT/components/cmeps/med_test_comps/xrof case_comps env_case.xml @@ -248,7 +249,7 @@ $SRCROOT/components/slim/ $SRCROOT/components/cdeps/dlnd $CIMEROOT/CIME/non_py/src/components/stub_comps_$COMP_INTERFACE/slnd - $CIMEROOT/CIME/non_py/src/components/xcpl_comps_$COMP_INTERFACE/xlnd + $SRCROOT/components/cmeps/med_test_comps/xlnd case_comps env_case.xml @@ -261,7 +262,7 @@ unset $CIMEROOT/CIME/non_py/src/components/stub_comps_$COMP_INTERFACE/siac - $CIMEROOT/CIME/non_py/src/components/xcpl_comps_$COMP_INTERFACE/xiac + $SRCROOT/components/cmeps/med_test_comps/xiac case_comps env_case.xml @@ -371,6 +372,7 @@ char $CIMEROOT/CIME/SystemTests + $SRCROOT/cime_config/SystemTests $COMP_ROOT_DIR_LND/cime_config/SystemTests $COMP_ROOT_DIR_ATM/cime_config/SystemTests $COMP_ROOT_DIR_OCN/cime_config/SystemTests diff --git a/CIME/data/config/config_headers.xml b/CIME/data/config/config_headers.xml index 10f59f2c770..748520cc93b 100644 --- a/CIME/data/config/config_headers.xml +++ b/CIME/data/config/config_headers.xml @@ -69,6 +69,7 @@ ROOTPE: the global mpi task of the component root task, if negative, indicates nodes rather than tasks. PSTRID: the stride of MPI tasks across the global set of pes (for now set to 1) NINST : the number of component instances (will be spread evenly across NTASKS) + EXCL_STRIDE: the stride of MPI tasks owned exclusively by a component. If 0, exclusivity is disabled. for example, for NTASKS = 8, NTHRDS = 2, ROOTPE = 32, NINST = 2 the MPI tasks would be placed starting on global pe 32 and each pe would be threaded 2-ways diff --git a/CIME/data/config/ufs/config_files.xml b/CIME/data/config/ufs/config_files.xml index 1f7137821fa..5f717ad6144 100644 --- a/CIME/data/config/ufs/config_files.xml +++ b/CIME/data/config/ufs/config_files.xml @@ -109,7 +109,7 @@ $SRCROOT/src/model/CDEPS/datm $CIMEROOT/CIME/non_py/src/components/stub_comps_$COMP_INTERFACE/satm - $CIMEROOT/CIME/non_py/src/components/xcpl_comps_$COMP_INTERFACE/xatm + $SRCROOT/components/cmeps/med_test_comps/xatm $SRCROOT/components/cam/ $SRCROOT/src/model/FV3 @@ -141,7 +141,7 @@ $SRCROOT/src/model/HYCOM/ $SRCROOT/src/model/CDEPS/docn $CIMEROOT/CIME/non_py/src/components/stub_comps_$COMP_INTERFACE/socn - $CIMEROOT/CIME/non_py/src/components/xcpl_comps_$COMP_INTERFACE/xocn + $SRCROOT/components/cmeps/med_test_comps/xocn case_comps env_case.xml @@ -156,7 +156,7 @@ $SRCROOT/components/ww3/ $SRCROOT/src/model/CDEPS/dwav $CIMEROOT/CIME/non_py/src/components/stub_comps_$COMP_INTERFACE/swav - $CIMEROOT/CIME/non_py/src/components/xcpl_comps_$COMP_INTERFACE/xwav + $SRCROOT/components/cmeps/med_test_comps/xwav case_comps env_case.xml @@ -171,7 +171,7 @@ $SRCROOT/components/cism/ $SRCROOT/src/model/CDEPS/dglc $CIMEROOT/CIME/non_py/src/components/stub_comps_$COMP_INTERFACE/sglc - $CIMEROOT/CIME/non_py/src/components/xcpl_comps_$COMP_INTERFACE/xglc + $SRCROOT/components/cmeps/med_test_comps/xglc case_comps env_case.xml @@ -186,7 +186,7 @@ $SRCROOT/src/model/CICE/ $SRCROOT/src/model/CDEPS/dice $CIMEROOT/CIME/non_py/src/components/stub_comps_$COMP_INTERFACE/sice - $CIMEROOT/CIME/non_py/src/components/xcpl_comps_$COMP_INTERFACE/xice + $SRCROOT/components/cmeps/med_test_comps/xice case_comps env_case.xml @@ -202,7 +202,7 @@ $SRCROOT/components/mosart/ $SRCROOT/src/model/CDEPS/drof $CIMEROOT/CIME/non_py/src/components/stub_comps_$COMP_INTERFACE/srof - $CIMEROOT/CIME/non_py/src/components/xcpl_comps_$COMP_INTERFACE/xrof + $SRCROOT/components/cmeps/med_test_comps/xrof case_comps env_case.xml @@ -217,7 +217,7 @@ $SRCROOT/components/clm/ $SRCROOT/src/model/CDEPS/dlnd $CIMEROOT/CIME/non_py/src/components/stub_comps_$COMP_INTERFACE/slnd - $CIMEROOT/CIME/non_py/src/components/xcpl_comps_$COMP_INTERFACE/xlnd + $SRCROOT/components/cmeps/med_test_comps/xlnd case_comps env_case.xml @@ -230,7 +230,7 @@ unset $CIMEROOT/CIME/non_py/src/components/stub_comps_$COMP_INTERFACE/siac - $CIMEROOT/CIME/non_py/src/components/xcpl_comps_$COMP_INTERFACE/xiac + $SRCROOT/components/cmeps/med_test_comps/xiac case_comps env_case.xml diff --git a/CIME/get_timing.py b/CIME/get_timing.py index a5526ccec8b..92ac32509de 100644 --- a/CIME/get_timing.py +++ b/CIME/get_timing.py @@ -411,6 +411,8 @@ def _getTiming(self, inst=0): "timing", "{}.ESMF_Profile.summary.{}".format(cime_model, self.lid), ) + else: + raise RuntimeError("Unknown driver set") foutfilename = os.path.join( self.caseroot, @@ -450,11 +452,15 @@ def _getTiming(self, inst=0): logger.warning("Unknown NCPL_BASE_PERIOD={}".format(ncpl_base_period)) # at this point the routine becomes driver specific + if self._driver == "mct" or self._driver == "moab": nprocs, ncount = self.gettime2("CPL:CLOCK_ADVANCE ") nsteps = ncount / nprocs elif self._driver == "nuopc": nprocs, nsteps = self.gettime2("") + else: + raise RuntimeError("Unknown driver setting") + adays = nsteps * tlen / ncpl odays = nsteps * tlen / ncpl if ocn_ncpl and inittype == "TRUE": @@ -547,7 +553,7 @@ def _getTiming(self, inst=0): fmax = self.gettime("[ensemble] FinalizePhase1")[1] xmax = self.getCOMMtime(inst_label[1:]) - if self._driver == "mct" or self._driver == "moab": + elif self._driver == "mct" or self._driver == "moab": for k in components: if k != "CPL": m = self.models[k] @@ -576,6 +582,8 @@ def _getTiming(self, inst=0): tmax = tmax + wtmin + correction ocn.tmax += ocnrunitime + else: + raise RuntimeError("driver not recognized or not defined") for m in self.models.values(): m.tmaxr = 0 diff --git a/CIME/hist_utils.py b/CIME/hist_utils.py index 8f76c61ec54..fa48f942c56 100644 --- a/CIME/hist_utils.py +++ b/CIME/hist_utils.py @@ -5,6 +5,7 @@ import os import re import filecmp +import shutil from CIME.XML.standard_module_setup import * from CIME.config import Config @@ -101,7 +102,7 @@ def copy_histfiles(case, suffix, match_suffix=None): if "ocean_geometry" in test_hist: comments += " skipping '{}'\n".format(test_hist) continue - if "mom6.ic" in test_hist: + if re.search(r"mom6(_[0-9]{4})?\.ic", os.path.basename(test_hist)): comments += " skipping '{}'\n".format(test_hist) continue comments += " Copying hist files for model '{}'\n".format(model) @@ -522,6 +523,7 @@ def cprnc( out = fd.read() comment = "" + files_match = False if cpr_stat == 0: # Successful exit from cprnc if multiinst_driver_compare: @@ -650,6 +652,20 @@ def _generate_baseline_impl(case, baseline_dir=None, allow_baseline_overwrite=Fa ): expect(False, " Cowardly refusing to overwrite existing baseline directory") + # Remove stale baseline files from a previous run so they don't linger when the + # new run no longer produces them. CaseDocs and user_nl* are managed by the + # namelist-generation phase (which already ran), so we preserve those. + preserve_list = ["CaseDocs", BLESS_LOG_NAME, TEST_STATUS_FILENAME] + if os.path.isdir(basegen_dir): + for item in os.listdir(basegen_dir): + if item in preserve_list or item.startswith("user_nl"): + continue + item_path = os.path.join(basegen_dir, item) + if os.path.isfile(item_path): + os.remove(item_path) + elif os.path.isdir(item_path): + shutil.rmtree(item_path) + comments = "Generating baselines into '{}'\n".format(basegen_dir) num_gen = 0 for model in _iter_model_file_substrs(case): diff --git a/CIME/namelist.py b/CIME/namelist.py index 94b9e02d151..486955bca69 100644 --- a/CIME/namelist.py +++ b/CIME/namelist.py @@ -1214,12 +1214,15 @@ def write( def _write(self, out_file, groups, format_, sorted_groups): """Unwrapped version of `write` assuming that a file object is input.""" + if groups is None: groups = list(self._groups.keys()) if format_ == "nml" or format_ == "nmlcontents": equals = " =" elif format_ == "rc": equals = ":" + else: + raise RuntimeError("format undefined or unrecognised") if sorted_groups: group_names = sorted(group for group in groups) else: diff --git a/CIME/non_py/src/components/xcpl_comps_nuopc/xatm/cime_config/buildlib b/CIME/non_py/src/components/xcpl_comps_nuopc/xatm/cime_config/buildlib deleted file mode 120000 index 7766f77f5bc..00000000000 --- a/CIME/non_py/src/components/xcpl_comps_nuopc/xatm/cime_config/buildlib +++ /dev/null @@ -1 +0,0 @@ -../../../../../../build_scripts/buildlib.internal_components \ No newline at end of file diff --git a/CIME/non_py/src/components/xcpl_comps_nuopc/xatm/cime_config/buildlib_cmake b/CIME/non_py/src/components/xcpl_comps_nuopc/xatm/cime_config/buildlib_cmake deleted file mode 120000 index 7766f77f5bc..00000000000 --- a/CIME/non_py/src/components/xcpl_comps_nuopc/xatm/cime_config/buildlib_cmake +++ /dev/null @@ -1 +0,0 @@ -../../../../../../build_scripts/buildlib.internal_components \ No newline at end of file diff --git a/CIME/non_py/src/components/xcpl_comps_nuopc/xatm/cime_config/buildnml b/CIME/non_py/src/components/xcpl_comps_nuopc/xatm/cime_config/buildnml deleted file mode 100755 index e7efcd46b01..00000000000 --- a/CIME/non_py/src/components/xcpl_comps_nuopc/xatm/cime_config/buildnml +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env python3 - -""" -build data model library -""" - -import sys, os - -_CIMEROOT = os.path.join( - os.path.dirname(os.path.abspath(__file__)), "..", "..", "..", "..", ".." -) -sys.path.append(os.path.join(_CIMEROOT, "CIME", "Tools")) - -from standard_script_setup import * -from CIME.buildnml import build_xcpl_nml, parse_input -from CIME.case import Case - - -def buildnml(case, caseroot, compname): - if compname != "xatm": - raise AttributeError - build_xcpl_nml(case, caseroot, compname) - - -def _main_func(): - caseroot = parse_input(sys.argv) - with Case(caseroot) as case: - buildnml(case, caseroot, "xatm") - - -if __name__ == "__main__": - _main_func() diff --git a/CIME/non_py/src/components/xcpl_comps_nuopc/xatm/cime_config/config_component.xml b/CIME/non_py/src/components/xcpl_comps_nuopc/xatm/cime_config/config_component.xml deleted file mode 100644 index 76c8be8c7f9..00000000000 --- a/CIME/non_py/src/components/xcpl_comps_nuopc/xatm/cime_config/config_component.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - Dead atm component - - - - char - xatm - xatm - case_comp - env_case.xml - Name of atmosphere component - - - - ========================================= - XATM naming conventions in compset name - ========================================= - - - diff --git a/CIME/non_py/src/components/xcpl_comps_nuopc/xatm/src/atm_comp_nuopc.F90 b/CIME/non_py/src/components/xcpl_comps_nuopc/xatm/src/atm_comp_nuopc.F90 deleted file mode 100644 index 64cd5b768da..00000000000 --- a/CIME/non_py/src/components/xcpl_comps_nuopc/xatm/src/atm_comp_nuopc.F90 +++ /dev/null @@ -1,529 +0,0 @@ -module atm_comp_nuopc - - !---------------------------------------------------------------------------- - ! This is the NUOPC cap for XATM - !---------------------------------------------------------------------------- - - use ESMF - use NUOPC , only : NUOPC_CompDerive, NUOPC_CompSetEntryPoint, NUOPC_CompSpecialize - use NUOPC , only : NUOPC_CompAttributeGet, NUOPC_Advertise - use NUOPC_Model , only : model_routine_SS => SetServices - use NUOPC_Model , only : model_label_Advance => label_Advance - use NUOPC_Model , only : model_label_SetRunClock => label_SetRunClock - use NUOPC_Model , only : model_label_Finalize => label_Finalize - use NUOPC_Model , only : NUOPC_ModelGet, SetVM - use shr_sys_mod , only : shr_sys_abort - use shr_kind_mod , only : r8=>shr_kind_r8, i8=>shr_kind_i8, cl=>shr_kind_cl, cs=>shr_kind_cs - use shr_log_mod , only : shr_log_getlogunit, shr_log_setlogunit - use dead_methods_mod , only : chkerr, state_setscalar, state_diagnose, alarmInit, memcheck - use dead_methods_mod , only : set_component_logging, get_component_instance, log_clock_advance - use dead_nuopc_mod , only : dead_read_inparms, ModelInitPhase, ModelSetRunClock - use dead_nuopc_mod , only : fld_list_add, fld_list_realize, fldsMax, fld_list_type - - implicit none - private ! except - - public :: SetServices - public :: SetVM - !-------------------------------------------------------------------------- - ! Private module data - !-------------------------------------------------------------------------- - - character(len=CL) :: flds_scalar_name = '' - integer :: flds_scalar_num = 0 - integer :: flds_scalar_index_nx = 0 - integer :: flds_scalar_index_ny = 0 - integer :: flds_scalar_index_nextsw_cday = 0 - - integer :: fldsToAtm_num = 0 - integer :: fldsFrAtm_num = 0 - type (fld_list_type) :: fldsToAtm(fldsMax) - type (fld_list_type) :: fldsFrAtm(fldsMax) - integer, parameter :: gridTofieldMap = 2 ! ungridded dimension is innermost - - type(ESMF_Mesh) :: mesh - integer :: nxg ! global dim i-direction - integer :: nyg ! global dim j-direction - integer :: my_task ! my task in mpi communicator mpicom - integer :: inst_index ! number of current instance (ie. 1) - character(len=5) :: inst_suffix ! char string associated with instance (ie. "_0001" or "") - integer :: logunit ! logging unit number - logical :: mastertask - integer :: dbug = 0 - character(*),parameter :: modName = "(xatm_comp_nuopc)" - character(*),parameter :: u_FILE_u = & - __FILE__ - -!=============================================================================== -contains -!=============================================================================== - - subroutine SetServices(gcomp, rc) - - type(ESMF_GridComp) :: gcomp - integer, intent(out) :: rc - character(len=*),parameter :: subname=trim(modName)//':(SetServices) ' - - rc = ESMF_SUCCESS - call ESMF_LogWrite(subname//' called', ESMF_LOGMSG_INFO, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! the NUOPC gcomp component will register the generic methods - call NUOPC_CompDerive(gcomp, model_routine_SS, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! switching to IPD versions - call ESMF_GridCompSetEntryPoint(gcomp, ESMF_METHOD_INITIALIZE, & - userRoutine=ModelInitPhase, phase=0, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! set entry point for methods that require specific implementation - call NUOPC_CompSetEntryPoint(gcomp, ESMF_METHOD_INITIALIZE, phaseLabelList=(/"IPDv01p1"/), & - userRoutine=InitializeAdvertise, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call NUOPC_CompSetEntryPoint(gcomp, ESMF_METHOD_INITIALIZE, phaseLabelList=(/"IPDv01p3"/), & - userRoutine=InitializeRealize, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! attach specializing method(s) - call NUOPC_CompSpecialize(gcomp, specLabel=model_label_Advance, specRoutine=ModelAdvance, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call ESMF_MethodRemove(gcomp, label=model_label_SetRunClock, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - call NUOPC_CompSpecialize(gcomp, specLabel=model_label_SetRunClock, specRoutine=ModelSetRunClock, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call NUOPC_CompSpecialize(gcomp, specLabel=model_label_Finalize, specRoutine=ModelFinalize, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call ESMF_LogWrite(subname//' done', ESMF_LOGMSG_INFO, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - end subroutine SetServices - - !=============================================================================== - subroutine InitializeAdvertise(gcomp, importState, exportState, clock, rc) - - ! input/output variables - type(ESMF_GridComp) :: gcomp - type(ESMF_State) :: importState, exportState - type(ESMF_Clock) :: clock - integer, intent(out) :: rc - - ! local variables - type(ESMF_VM) :: vm - character(CS) :: stdname - integer :: n - integer :: lsize ! local array size - integer :: shrlogunit ! original log unit - character(CL) :: cvalue - character(len=CL) :: logmsg - logical :: isPresent, isSet - character(len=*),parameter :: subname=trim(modName)//':(InitializeAdvertise) ' - !------------------------------------------------------------------------------- - - rc = ESMF_SUCCESS - - call ESMF_GridCompGet(gcomp, vm=vm, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call ESMF_VMGet(vm, localpet=my_task, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - mastertask = (my_task==0) - - ! determine instance information - call get_component_instance(gcomp, inst_suffix, inst_index, rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - - ! set logunit and set shr logging to my log file - call set_component_logging(gcomp, mastertask, logunit, shrlogunit, rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - - ! Initialize xatm - call dead_read_inparms('atm', inst_suffix, logunit, nxg, nyg) - - ! advertise import and export fields - call NUOPC_CompAttributeGet(gcomp, name="ScalarFieldName", value=cvalue, isPresent=isPresent, isSet=isSet, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - if (isPresent .and. isSet) then - flds_scalar_name = trim(cvalue) - call ESMF_LogWrite(trim(subname)//' flds_scalar_name = '//trim(flds_scalar_name), ESMF_LOGMSG_INFO) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - else - call shr_sys_abort(subname//'Need to set attribute ScalarFieldName') - endif - - call NUOPC_CompAttributeGet(gcomp, name="ScalarFieldCount", value=cvalue, isPresent=isPresent, isSet=isSet, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - if (isPresent .and. isSet) then - read(cvalue, *) flds_scalar_num - write(logmsg,*) flds_scalar_num - call ESMF_LogWrite(trim(subname)//' flds_scalar_num = '//trim(logmsg), ESMF_LOGMSG_INFO) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - else - call shr_sys_abort(subname//'Need to set attribute ScalarFieldCount') - endif - - call NUOPC_CompAttributeGet(gcomp, name="ScalarFieldIdxGridNX", value=cvalue, isPresent=isPresent, isSet=isSet, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - if (isPresent .and. isSet) then - read(cvalue,*) flds_scalar_index_nx - write(logmsg,*) flds_scalar_index_nx - call ESMF_LogWrite(trim(subname)//' : flds_scalar_index_nx = '//trim(logmsg), ESMF_LOGMSG_INFO) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - else - call shr_sys_abort(subname//'Need to set attribute ScalarFieldIdxGridNX') - endif - - call NUOPC_CompAttributeGet(gcomp, name="ScalarFieldIdxGridNY", value=cvalue, isPresent=isPresent, isSet=isSet, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - if (isPresent .and. isSet) then - read(cvalue,*) flds_scalar_index_ny - write(logmsg,*) flds_scalar_index_ny - call ESMF_LogWrite(trim(subname)//' : flds_scalar_index_ny = '//trim(logmsg), ESMF_LOGMSG_INFO) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - else - call shr_sys_abort(subname//'Need to set attribute ScalarFieldIdxGridNY') - endif - - call NUOPC_CompAttributeGet(gcomp, name="ScalarFieldIdxNextSwCday", value=cvalue, isPresent=isPresent, isSet=isSet, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - if (isPresent .and. isSet) then - read(cvalue,*) flds_scalar_index_nextsw_cday - write(logmsg,*) flds_scalar_index_nextsw_cday - call ESMF_LogWrite(trim(subname)//' : flds_scalar_index_nextsw_cday = '//trim(logmsg), ESMF_LOGMSG_INFO) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - else - call shr_sys_abort(subname//'Need to set attribute ScalarFieldIdxNextSwCday') - endif - - if (nxg /= 0 .and. nyg /= 0) then - - call fld_list_add(fldsFrAtm_num, fldsFrAtm, trim(flds_scalar_name)) - call fld_list_add(fldsFrAtm_num, fldsFrAtm, 'Sa_topo' ) - call fld_list_add(fldsFrAtm_num, fldsFrAtm, 'Sa_z' ) - call fld_list_add(fldsFrAtm_num, fldsFrAtm, 'Sa_u' ) - call fld_list_add(fldsFrAtm_num, fldsFrAtm, 'Sa_v' ) - call fld_list_add(fldsFrAtm_num, fldsFrAtm, 'Sa_tbot' ) - call fld_list_add(fldsFrAtm_num, fldsFrAtm, 'Sa_ptem' ) - call fld_list_add(fldsFrAtm_num, fldsFrAtm, 'Sa_shum' ) - call fld_list_add(fldsFrAtm_num, fldsFrAtm, 'Sa_pbot' ) - call fld_list_add(fldsFrAtm_num, fldsFrAtm, 'Sa_dens' ) - call fld_list_add(fldsFrAtm_num, fldsFrAtm, 'Sa_pslv' ) - call fld_list_add(fldsFrAtm_num, fldsFrAtm, 'Faxa_rainc' ) - call fld_list_add(fldsFrAtm_num, fldsFrAtm, 'Faxa_rainl' ) - call fld_list_add(fldsFrAtm_num, fldsFrAtm, 'Faxa_snowc' ) - call fld_list_add(fldsFrAtm_num, fldsFrAtm, 'Faxa_snowl' ) - call fld_list_add(fldsFrAtm_num, fldsFrAtm, 'Faxa_lwdn' ) - call fld_list_add(fldsFrAtm_num, fldsFrAtm, 'Faxa_swndr' ) - call fld_list_add(fldsFrAtm_num, fldsFrAtm, 'Faxa_swvdr' ) - call fld_list_add(fldsFrAtm_num, fldsFrAtm, 'Faxa_swndf' ) - call fld_list_add(fldsFrAtm_num, fldsFrAtm, 'Faxa_swvdf' ) - call fld_list_add(fldsFrAtm_num, fldsFrAtm, 'Faxa_swnet' ) - call fld_list_add(fldsFrAtm_num, fldsFrAtm, 'Faxa_bcph' , ungridded_lbound=1, ungridded_ubound=3) - call fld_list_add(fldsFrAtm_num, fldsFrAtm, 'Faxa_ocph' , ungridded_lbound=1, ungridded_ubound=3) - call fld_list_add(fldsFrAtm_num, fldsFrAtm, 'Faxa_dstwet', ungridded_lbound=1, ungridded_ubound=4) - call fld_list_add(fldsFrAtm_num, fldsFrAtm, 'Faxa_dstdry', ungridded_lbound=1, ungridded_ubound=4) - - call fld_list_add(fldsToAtm_num, fldsToAtm, trim(flds_scalar_name)) - call fld_list_add(fldsToAtm_num, fldsToAtm, 'Sx_anidr' ) - call fld_list_add(fldsToAtm_num, fldsToAtm, 'Sx_avsdf' ) - call fld_list_add(fldsToAtm_num, fldsToAtm, 'Sx_anidf' ) - call fld_list_add(fldsToAtm_num, fldsToAtm, 'Sx_avsdr' ) - call fld_list_add(fldsToAtm_num, fldsToAtm, 'Sl_lfrac' ) - call fld_list_add(fldsToAtm_num, fldsToAtm, 'Si_ifrac' ) - call fld_list_add(fldsToAtm_num, fldsToAtm, 'So_ofrac' ) - call fld_list_add(fldsToAtm_num, fldsToAtm, 'Sx_tref' ) - call fld_list_add(fldsToAtm_num, fldsToAtm, 'Sx_qref' ) - call fld_list_add(fldsToAtm_num, fldsToAtm, 'Sx_t' ) - call fld_list_add(fldsToAtm_num, fldsToAtm, 'So_t' ) - call fld_list_add(fldsToAtm_num, fldsToAtm, 'Sl_fv' ) - call fld_list_add(fldsToAtm_num, fldsToAtm, 'Sl_ram1' ) - call fld_list_add(fldsToAtm_num, fldsToAtm, 'Sl_snowh' ) - call fld_list_add(fldsToAtm_num, fldsToAtm, 'Si_snowh' ) - call fld_list_add(fldsToAtm_num, fldsToAtm, 'So_ssq' ) - call fld_list_add(fldsToAtm_num, fldsToAtm, 'So_re' ) - call fld_list_add(fldsToAtm_num, fldsToAtm, 'Sx_u10' ) - call fld_list_add(fldsToAtm_num, fldsToAtm, 'Faxx_taux' ) - call fld_list_add(fldsToAtm_num, fldsToAtm, 'Faxx_tauy' ) - call fld_list_add(fldsToAtm_num, fldsToAtm, 'Faxx_lat' ) - call fld_list_add(fldsToAtm_num, fldsToAtm, 'Faxx_sen' ) - call fld_list_add(fldsToAtm_num, fldsToAtm, 'Faxx_lwup' ) - call fld_list_add(fldsToAtm_num, fldsToAtm, 'Faxx_evap' ) - - do n = 1,fldsFrAtm_num - if(mastertask) write(logunit,*)'Advertising From Xatm ',trim(fldsFrAtm(n)%stdname) - call NUOPC_Advertise(exportState, standardName=fldsFrAtm(n)%stdname, & - TransferOfferGeomObject='will provide', rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - end do - - do n = 1,fldsToAtm_num - if(mastertask) write(logunit,*)'Advertising To Xatm',trim(fldsToAtm(n)%stdname) - call NUOPC_Advertise(importState, standardName=fldsToAtm(n)%stdname, & - TransferOfferGeomObject='will provide', rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - enddo - end if - - ! Reset shr logging to original values - call shr_log_setLogUnit (shrlogunit) - - end subroutine InitializeAdvertise - - !=============================================================================== - subroutine InitializeRealize(gcomp, importState, exportState, clock, rc) - - ! input/output arguments - type(ESMF_GridComp) :: gcomp - type(ESMF_State) :: importState, exportState - type(ESMF_Clock) :: clock - integer, intent(out) :: rc - - ! local variables - type(ESMF_Time) :: nextTime - real(r8) :: nextsw_cday - integer :: n - integer :: shrlogunit ! original log unit - character(ESMF_MAXSTR) :: cvalue ! config data - character(len=*),parameter :: subname=trim(modName)//':(InitializeRealize: xatm) ' - !------------------------------------------------------------------------------- - - rc = ESMF_SUCCESS - - ! Reset shr logging to my log file - call shr_log_getLogUnit (shrlogunit) - call shr_log_setLogUnit (logUnit) - - ! generate the mesh - call NUOPC_CompAttributeGet(gcomp, name='mesh_atm', value=cvalue, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - - mesh = ESMF_MeshCreate(filename=trim(cvalue), fileformat=ESMF_FILEFORMAT_ESMFMESH, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - ! realize the actively coupled fields, now that a mesh is established - ! NUOPC_Realize "realizes" a previously advertised field in the importState and exportState - ! by replacing the advertised fields with the newly created fields of the same name. - call fld_list_realize( & - state=exportState, & - fldList=fldsFrAtm, & - numflds=fldsFrAtm_num, & - flds_scalar_name=flds_scalar_name, & - flds_scalar_num=flds_scalar_num, & - tag=subname//':xatmExport',& - mesh=mesh, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call fld_list_realize( & - state=importState, & - fldList=fldsToAtm, & - numflds=fldsToAtm_num, & - flds_scalar_name=flds_scalar_name, & - flds_scalar_num=flds_scalar_num, & - tag=subname//':xatmImport',& - mesh=mesh, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! Pack export state - call state_setexport(exportState, rc=rc) - call State_SetScalar(dble(nxg),flds_scalar_index_nx, exportState, flds_scalar_name, flds_scalar_num, rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - call State_SetScalar(dble(nyg),flds_scalar_index_ny, exportState, flds_scalar_name, flds_scalar_num, rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! Set time of next radiation computation - call ESMF_ClockGetNextTime(clock, nextTime) - if (chkerr(rc,__LINE__,u_FILE_u)) return - call ESMF_TimeGet(nextTime, dayOfYear_r8=nextsw_cday) - if (chkerr(rc,__LINE__,u_FILE_u)) return - call State_SetScalar(nextsw_cday, flds_scalar_index_nextsw_cday, exportState, & - flds_scalar_name, flds_scalar_num, rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! diagnostics - if (dbug > 1) then - call State_diagnose(exportState,subname//':ES',rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - endif - - call shr_log_setLogUnit (shrlogunit) - - end subroutine InitializeRealize - - !=============================================================================== - subroutine ModelAdvance(gcomp, rc) - - ! input/output variables - type(ESMF_GridComp) :: gcomp - integer, intent(out) :: rc - - ! local variables - type(ESMF_Clock) :: clock - type(ESMF_State) :: exportState - real(r8) :: nextsw_cday - integer :: shrlogunit ! original log unit - character(len=*),parameter :: subname=trim(modName)//':(ModelAdvance) ' - !------------------------------------------------------------------------------- - - rc = ESMF_SUCCESS - - if (dbug > 1) then - call ESMF_LogWrite(subname//' called', ESMF_LOGMSG_INFO, rc=rc) - end if - call memcheck(subname, 3, mastertask) - - call shr_log_getLogUnit (shrlogunit) - call shr_log_setLogUnit (logunit) - - !-------------------------------- - ! Pack export state - !-------------------------------- - - call NUOPC_ModelGet(gcomp, modelClock=clock, exportState=exportState, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call state_setexport(exportState, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call State_SetScalar(nextsw_cday, flds_scalar_index_nextsw_cday, exportState, & - flds_scalar_name, flds_scalar_num, rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - !-------------------------------- - ! diagnostics - !-------------------------------- - - if (dbug > 1) then - call state_diagnose(exportState,subname//':ES',rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - if (mastertask) then - call log_clock_advance(clock, 'XATM', logunit, rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - endif - endif - - call shr_log_setLogUnit (shrlogunit) - - end subroutine ModelAdvance - - !=============================================================================== - subroutine state_setexport(exportState, rc) - - ! input/output variables - type(ESMF_State) , intent(inout) :: exportState - integer, intent(out) :: rc - - ! local variables - integer :: n, nf, nind - real(r8), pointer :: lat(:) - real(r8), pointer :: lon(:) - integer :: spatialDim - integer :: numOwnedElements - real(R8), pointer :: ownedElemCoords(:) - !-------------------------------------------------- - - rc = ESMF_SUCCESS - - call ESMF_MeshGet(mesh, spatialDim=spatialDim, numOwnedElements=numOwnedElements, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - allocate(ownedElemCoords(spatialDim*numOwnedElements)) - call ESMF_MeshGet(mesh, ownedElemCoords=ownedElemCoords) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - - allocate(lon(numownedElements)) - allocate(lat(numownedElements)) - do n = 1,numownedElements - lon(n) = ownedElemCoords(2*n-1) - lat(n) = ownedElemCoords(2*n) - end do - - ! Start from index 2 in order to Skip the scalar field here - do nf = 2,fldsFrAtm_num - if (fldsFrAtm(nf)%ungridded_ubound == 0) then - call field_setexport(exportState, trim(fldsFrAtm(nf)%stdname), lon, lat, nf=nf, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - else - do nind = 1,fldsFrAtm(nf)%ungridded_ubound - call field_setexport(exportState, trim(fldsFrAtm(nf)%stdname), lon, lat, nf=nf+nind-1, & - ungridded_index=nind, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - end do - end if - end do - - deallocate(lon) - deallocate(lat) - - end subroutine state_setexport - - !=============================================================================== - subroutine field_setexport(exportState, fldname, lon, lat, nf, ungridded_index, rc) - - use shr_const_mod , only : pi=>shr_const_pi - - ! intput/otuput variables - type(ESMF_State) , intent(inout) :: exportState - character(len=*) , intent(in) :: fldname - real(r8) , intent(in) :: lon(:) - real(r8) , intent(in) :: lat(:) - integer , intent(in) :: nf - integer, optional , intent(in) :: ungridded_index - integer , intent(out) :: rc - - ! local variables - integer :: i, ncomp - type(ESMF_Field) :: lfield - real(r8), pointer :: data1d(:) - real(r8), pointer :: data2d(:,:) - !-------------------------------------------------- - - rc = ESMF_SUCCESS - - call ESMF_StateGet(exportState, itemName=trim(fldname), field=lfield, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ncomp = 1 - if (present(ungridded_index)) then - call ESMF_FieldGet(lfield, farrayPtr=data2d, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - if (gridToFieldMap == 1) then - do i = 1,size(data2d, dim=1) - data2d(i,ungridded_index) = (nf*100) * cos(pi*lat(i)/180.0_R8) * & - sin((pi*lon(i)/180.0_R8) - (ncomp-1)*(pi/3.0_R8) ) + (ncomp*10.0_R8) - end do - else if (gridToFieldMap == 2) then - do i = 1,size(data2d, dim=2) - data2d(ungridded_index,i) = (nf*100) * cos(pi*lat(i)/180.0_R8) * & - sin((pi*lon(i)/180.0_R8) - (ncomp-1)*(pi/3.0_R8) ) + (ncomp*10.0_R8) - end do - end if - else - call ESMF_FieldGet(lfield, farrayPtr=data1d, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - do i = 1,size(data1d) - data1d(i) = (nf*100) * cos(pi*lat(i)/180.0_R8) * & - sin((pi*lon(i)/180.0_R8) - (ncomp-1)*(pi/3.0_R8) ) + (ncomp*10.0_R8) - end do - end if - - end subroutine field_setexport - - !=============================================================================== - subroutine ModelFinalize(gcomp, rc) - type(ESMF_GridComp) :: gcomp - integer, intent(out) :: rc - !------------------------------------------------------------------------------- - - rc = ESMF_SUCCESS - - if (mastertask) then - write(logunit,*) - write(logunit,*) 'xatm: end of main integration loop' - write(logunit,*) - end if - end subroutine ModelFinalize - -end module atm_comp_nuopc diff --git a/CIME/non_py/src/components/xcpl_comps_nuopc/xglc/cime_config/buildlib b/CIME/non_py/src/components/xcpl_comps_nuopc/xglc/cime_config/buildlib deleted file mode 120000 index 7766f77f5bc..00000000000 --- a/CIME/non_py/src/components/xcpl_comps_nuopc/xglc/cime_config/buildlib +++ /dev/null @@ -1 +0,0 @@ -../../../../../../build_scripts/buildlib.internal_components \ No newline at end of file diff --git a/CIME/non_py/src/components/xcpl_comps_nuopc/xglc/cime_config/buildlib_cmake b/CIME/non_py/src/components/xcpl_comps_nuopc/xglc/cime_config/buildlib_cmake deleted file mode 120000 index 7766f77f5bc..00000000000 --- a/CIME/non_py/src/components/xcpl_comps_nuopc/xglc/cime_config/buildlib_cmake +++ /dev/null @@ -1 +0,0 @@ -../../../../../../build_scripts/buildlib.internal_components \ No newline at end of file diff --git a/CIME/non_py/src/components/xcpl_comps_nuopc/xglc/cime_config/buildnml b/CIME/non_py/src/components/xcpl_comps_nuopc/xglc/cime_config/buildnml deleted file mode 100755 index a90f7a189ef..00000000000 --- a/CIME/non_py/src/components/xcpl_comps_nuopc/xglc/cime_config/buildnml +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env python3 - -""" -build data model library -""" - -import sys, os - -_CIMEROOT = os.path.join( - os.path.dirname(os.path.abspath(__file__)), "..", "..", "..", "..", ".." -) -sys.path.append(os.path.join(_CIMEROOT, "CIME", "Tools")) - -from standard_script_setup import * -from CIME.buildnml import build_xcpl_nml, parse_input -from CIME.case import Case - - -def buildnml(case, caseroot, compname): - if compname != "xglc": - raise AttributeError - build_xcpl_nml(case, caseroot, compname) - - -def _main_func(): - caseroot = parse_input(sys.argv) - with Case(caseroot) as case: - buildnml(case, caseroot, "xglc") - - -if __name__ == "__main__": - _main_func() diff --git a/CIME/non_py/src/components/xcpl_comps_nuopc/xglc/cime_config/config_component.xml b/CIME/non_py/src/components/xcpl_comps_nuopc/xglc/cime_config/config_component.xml deleted file mode 100644 index f1765811b70..00000000000 --- a/CIME/non_py/src/components/xcpl_comps_nuopc/xglc/cime_config/config_component.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - Dead land-ice component - - - - char - xglc - xglc - case_comp - env_case.xml - Name of land-ice component - - - - - ========================================= - XGLC naming conventions in compset name - ========================================= - - - diff --git a/CIME/non_py/src/components/xcpl_comps_nuopc/xglc/src/glc_comp_nuopc.F90 b/CIME/non_py/src/components/xcpl_comps_nuopc/xglc/src/glc_comp_nuopc.F90 deleted file mode 100644 index 4b498f8d9c6..00000000000 --- a/CIME/non_py/src/components/xcpl_comps_nuopc/xglc/src/glc_comp_nuopc.F90 +++ /dev/null @@ -1,457 +0,0 @@ -module glc_comp_nuopc - - !---------------------------------------------------------------------------- - ! This is the NUOPC cap for XGLC - !---------------------------------------------------------------------------- - - use ESMF - use NUOPC , only : NUOPC_CompDerive, NUOPC_CompSetEntryPoint, NUOPC_CompSpecialize - use NUOPC , only : NUOPC_CompAttributeGet, NUOPC_Advertise, NUOPC_AddNestedState - use NUOPC_Model , only : model_routine_SS => SetServices - use NUOPC_Model , only : model_label_Advance => label_Advance - use NUOPC_Model , only : model_label_SetRunClock => label_SetRunClock - use NUOPC_Model , only : model_label_Finalize => label_Finalize - use NUOPC_Model , only : NUOPC_ModelGet, SetVM - use shr_sys_mod , only : shr_sys_abort - use shr_kind_mod , only : r8=>shr_kind_r8, i8=>shr_kind_i8, cl=>shr_kind_cl, cs=>shr_kind_cs - use shr_log_mod , only : shr_log_getlogunit, shr_log_setlogunit - use dead_methods_mod , only : chkerr, state_setscalar, state_diagnose, alarmInit, memcheck - use dead_methods_mod , only : set_component_logging, get_component_instance, log_clock_advance - use dead_nuopc_mod , only : dead_read_inparms, ModelInitPhase, ModelSetRunClock - use dead_nuopc_mod , only : fld_list_add, fld_list_realize, fldsMax, fld_list_type - - implicit none - private ! except - - public :: SetServices - public :: SetVM - !-------------------------------------------------------------------------- - ! Private module data - !-------------------------------------------------------------------------- - - character(len=CL) :: flds_scalar_name = '' - integer :: flds_scalar_num = 0 - integer :: flds_scalar_index_nx = 0 - integer :: flds_scalar_index_ny = 0 - - integer :: fldsToGlc_num = 0 - integer :: fldsFrGlc_num = 0 - type (fld_list_type) :: fldsToGlc(fldsMax) - type (fld_list_type) :: fldsFrGlc(fldsMax) - integer, parameter :: gridTofieldMap = 2 ! ungridded dimension is innermost - - type(ESMF_Mesh) :: mesh - integer :: nxg ! global dim i-direction - integer :: nyg ! global dim j-direction - integer :: my_task ! my task in mpi communicator mpicom - integer :: inst_index ! number of current instance (ie. 1) - character(len=16) :: inst_suffix = "" ! char string associated with instance (ie. "_0001" or "") - integer :: logunit ! logging unit number - integer ,parameter :: master_task=0 ! task number of master task - logical :: mastertask - integer :: dbug = 0 - character(*),parameter :: modName = "(xglc_comp_nuopc)" - character(*),parameter :: u_FILE_u = & - __FILE__ - - ! TODO: this must be generalized - but for now is just hard-wired - integer, parameter :: max_icesheets = 1 - integer :: num_icesheets = 1 - type(ESMF_State) :: NStateImp(max_icesheets) - type(ESMF_State) :: NStateExp(max_icesheets) - -!=============================================================================== -contains -!=============================================================================== - - subroutine SetServices(gcomp, rc) - - type(ESMF_GridComp) :: gcomp - integer, intent(out) :: rc - character(len=*),parameter :: subname=trim(modName)//':(SetServices) ' - - rc = ESMF_SUCCESS - call ESMF_LogWrite(subname//' called', ESMF_LOGMSG_INFO, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! the NUOPC gcomp component will register the generic methods - call NUOPC_CompDerive(gcomp, model_routine_SS, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! switching to IPD versions - call ESMF_GridCompSetEntryPoint(gcomp, ESMF_METHOD_INITIALIZE, & - userRoutine=ModelInitPhase, phase=0, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! set entry point for methods that require specific implementation - call NUOPC_CompSetEntryPoint(gcomp, ESMF_METHOD_INITIALIZE, phaseLabelList=(/"IPDv01p1"/), & - userRoutine=InitializeAdvertise, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call NUOPC_CompSetEntryPoint(gcomp, ESMF_METHOD_INITIALIZE, phaseLabelList=(/"IPDv01p3"/), & - userRoutine=InitializeRealize, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! attach specializing method(s) - call NUOPC_CompSpecialize(gcomp, specLabel=model_label_Advance, specRoutine=ModelAdvance, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call ESMF_MethodRemove(gcomp, label=model_label_SetRunClock, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - call NUOPC_CompSpecialize(gcomp, specLabel=model_label_SetRunClock, specRoutine=ModelSetRunClock, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call NUOPC_CompSpecialize(gcomp, specLabel=model_label_Finalize, specRoutine=ModelFinalize, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call ESMF_LogWrite(subname//' done', ESMF_LOGMSG_INFO, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - end subroutine SetServices - - !=============================================================================== - subroutine InitializeAdvertise(gcomp, importState, exportState, clock, rc) - - ! input/output variables - type(ESMF_GridComp) :: gcomp - type(ESMF_State) :: importState, exportState - type(ESMF_Clock) :: clock - integer, intent(out) :: rc - - ! local variables - type(ESMF_VM) :: vm - character(CS) :: stdname - integer :: n, ns, nf - integer :: lsize ! local array size - integer :: shrlogunit ! original log unit - character(CL) :: cvalue - character(len=CL) :: logmsg - character(len=CS) :: cnum - logical :: isPresent, isSet - character(len=*),parameter :: subname=trim(modName)//':(InitializeAdvertise) ' - !------------------------------------------------------------------------------- - - rc = ESMF_SUCCESS - - call ESMF_GridCompGet(gcomp, vm=vm, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call ESMF_VMGet(vm, localpet=my_task, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - mastertask = (my_task == master_task) - - ! determine instance information - call get_component_instance(gcomp, inst_suffix, inst_index, rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! set logunit and set shr logging to my log file - call set_component_logging(gcomp, my_task==master_task, logunit, shrlogunit, rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! Initialize xglc - call dead_read_inparms('glc', inst_suffix, logunit, nxg, nyg) - - ! advertise import and export fields - call NUOPC_CompAttributeGet(gcomp, name="ScalarFieldName", value=cvalue, isPresent=isPresent, isSet=isSet, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - if (isPresent .and. isSet) then - flds_scalar_name = trim(cvalue) - call ESMF_LogWrite(trim(subname)//' flds_scalar_name = '//trim(flds_scalar_name), ESMF_LOGMSG_INFO) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - else - call shr_sys_abort(subname//'Need to set attribute ScalarFieldName') - endif - - call NUOPC_CompAttributeGet(gcomp, name="ScalarFieldCount", value=cvalue, isPresent=isPresent, isSet=isSet, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - if (isPresent .and. isSet) then - read(cvalue, *) flds_scalar_num - write(logmsg,*) flds_scalar_num - call ESMF_LogWrite(trim(subname)//' flds_scalar_num = '//trim(logmsg), ESMF_LOGMSG_INFO) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - else - call shr_sys_abort(subname//'Need to set attribute ScalarFieldCount') - endif - - call NUOPC_CompAttributeGet(gcomp, name="ScalarFieldIdxGridNX", value=cvalue, isPresent=isPresent, isSet=isSet, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - if (isPresent .and. isSet) then - read(cvalue,*) flds_scalar_index_nx - write(logmsg,*) flds_scalar_index_nx - call ESMF_LogWrite(trim(subname)//' : flds_scalar_index_nx = '//trim(logmsg), ESMF_LOGMSG_INFO) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - else - call shr_sys_abort(subname//'Need to set attribute ScalarFieldIdxGridNX') - endif - - call NUOPC_CompAttributeGet(gcomp, name="ScalarFieldIdxGridNY", value=cvalue, isPresent=isPresent, isSet=isSet, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - if (isPresent .and. isSet) then - read(cvalue,*) flds_scalar_index_ny - write(logmsg,*) flds_scalar_index_ny - call ESMF_LogWrite(trim(subname)//' : flds_scalar_index_ny = '//trim(logmsg), ESMF_LOGMSG_INFO) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - else - call shr_sys_abort(subname//'Need to set attribute ScalarFieldIdxGridNY') - endif - - ! Create nested state for each active ice sheet - do ns = 1,num_icesheets - write(cnum,'(i0)') ns - call NUOPC_AddNestedState(importState, CplSet="GLC"//trim(cnum), nestedState=NStateImp(ns), rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - call NUOPC_AddNestedState(exportState, CplSet="GLC"//trim(cnum), nestedState=NStateExp(ns), rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - end do - - if (nxg /= 0 .and. nyg /= 0) then - - call fld_list_add(fldsFrGlc_num, fldsFrGlc, trim(flds_scalar_name)) - call fld_list_add(fldsFrGlc_num, fldsFrGlc, 'Sg_icemask' ) - call fld_list_add(fldsFrGlc_num, fldsFrGlc, 'Sg_icemask_coupled_fluxes' ) - call fld_list_add(fldsFrGlc_num, fldsFrGlc, 'Sg_ice_covered' ) - call fld_list_add(fldsFrGlc_num, fldsFrGlc, 'Sg_topo' ) - call fld_list_add(fldsFrGlc_num, fldsFrGlc, 'Flgg_hflx' ) - - call fld_list_add(fldsToGlc_num, fldsToGlc, trim(flds_scalar_name)) - call fld_list_add(fldsToGlc_num, fldsToGlc, 'Sl_tsrf') - call fld_list_add(fldsToGlc_num, fldsToGlc, 'Flgl_qice') - - ! Now advertise import and export fields fields - do ns = 1,num_icesheets - if (mastertask) write(logunit,*)'Advertising To Xglc ',trim(fldsToGlc(ns)%stdname) - do nf = 1,fldsToGlc_num - call NUOPC_Advertise(NStateImp(ns), standardName=fldsToGlc(nf)%stdname, & - TransferOfferGeomObject='will provide', rc=rc) - if (chkErr(rc,__LINE__,u_FILE_u)) return - end do - if (mastertask) write(logunit,*)'Advertising From Xglc ',trim(fldsFrGlc(ns)%stdname) - do nf = 1,fldsFrGlc_num - call NUOPC_Advertise(NStateExp(ns), standardName=fldsFrGlc(nf)%stdname, & - TransferOfferGeomObject='will provide', rc=rc) - if (chkErr(rc,__LINE__,u_FILE_u)) return - end do - enddo - - end if - - end subroutine InitializeAdvertise - - !=============================================================================== - - subroutine InitializeRealize(gcomp, importState, exportState, clock, rc) - - ! input/output variables - type(ESMF_GridComp) :: gcomp - type(ESMF_State) :: importState, exportState - type(ESMF_Clock) :: clock - integer, intent(out) :: rc - - ! local variables - integer :: n, ns - character(ESMF_MAXSTR) :: cvalue ! config data - character(len=*),parameter :: subname=trim(modName)//':(InitializeRealize) ' - !------------------------------------------------------------------------------- - - rc = ESMF_SUCCESS - - ! generate the mesh - call NUOPC_CompAttributeGet(gcomp, name='mesh_glc', value=cvalue, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - - mesh = ESMF_MeshCreate(filename=trim(cvalue), fileformat=ESMF_FILEFORMAT_ESMFMESH, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - - ! realize the actively coupled fields, now that a mesh is established - ! NUOPC_Realize "realizes" a previously advertised field in the importState and exportState - ! by replacing the advertised fields with the newly created fields of the same name. - do ns = 1,num_icesheets - call fld_list_realize( & - state=NStateExp(ns), & - fldList=fldsFrGlc, & - numflds=fldsFrGlc_num, & - flds_scalar_name=flds_scalar_name, & - flds_scalar_num=flds_scalar_num, & - tag=subname//':dglcExport',& - mesh=mesh, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call fld_list_realize( & - state=NStateImp(ns), & - fldList=fldsToGlc, & - numflds=fldsToGlc_num, & - flds_scalar_name=flds_scalar_name, & - flds_scalar_num=flds_scalar_num, & - tag=subname//':dglcImport',& - mesh=mesh, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - end do - - ! Pack export state and set the coupling scalars - call state_setexport(rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - do ns = 1,num_icesheets - call state_setscalar(dble(nxg),flds_scalar_index_nx, NStateExp(ns), flds_scalar_name, flds_scalar_num, rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - call state_setscalar(dble(nyg),flds_scalar_index_ny, NStateExp(ns), flds_scalar_name, flds_scalar_num, rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - end do - - end subroutine InitializeRealize - - !=============================================================================== - - subroutine ModelAdvance(gcomp, rc) - - ! input/output variables - type(ESMF_GridComp) :: gcomp - integer, intent(out) :: rc - - ! local variables - character(len=*),parameter :: subname=trim(modName)//':(ModelAdvance) ' - !------------------------------------------------------------------------------- - - rc = ESMF_SUCCESS - call memcheck(subname, 3, mastertask) - - call state_setexport(rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - end subroutine ModelAdvance - - !=============================================================================== - subroutine state_setexport(rc) - - ! input/output variables - integer, intent(out) :: rc - - ! local variables - integer :: n, nf, nind, ns - real(r8), pointer :: lat(:) - real(r8), pointer :: lon(:) - integer :: spatialDim - integer :: numOwnedElements - real(R8), pointer :: ownedElemCoords(:) - character(len=*),parameter :: subname=trim(modName)//':(state_setexport) ' - !-------------------------------------------------- - rc = ESMF_SUCCESS - - call ESMF_MeshGet(mesh, spatialDim=spatialDim, numOwnedElements=numOwnedElements, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - allocate(ownedElemCoords(spatialDim*numOwnedElements)) - call ESMF_MeshGet(mesh, ownedElemCoords=ownedElemCoords) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - - allocate(lon(numownedElements)) - allocate(lat(numownedElements)) - do n = 1,numownedElements - lon(n) = ownedElemCoords(2*n-1) - lat(n) = ownedElemCoords(2*n) - end do - - ! Start from index 2 in order to skip the scalar field - do ns = 1,num_icesheets - do nf = 2,fldsFrGlc_num - if (fldsFrGlc(nf)%ungridded_ubound == 0) then - call field_setexport(NStateExp(ns), trim(fldsFrGlc(nf)%stdname), lon, lat, nf=nf, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - else - do nind = 1,fldsFrGlc(nf)%ungridded_ubound - call field_setexport(NStateExp(ns), trim(fldsFrGlc(nf)%stdname), lon, lat, nf=nf+nind-1, & - ungridded_index=nind, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - end do - end if - end do - if (dbug > 1) then - call State_diagnose(NStateExp(ns), trim(subname)//':ES',rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - endif - end do - - deallocate(lon) - deallocate(lat) - - end subroutine state_setexport - - !=============================================================================== - - subroutine field_setexport(exportState, fldname, lon, lat, nf, ungridded_index, rc) - - use shr_const_mod , only : pi=>shr_const_pi - - ! intput/otuput variables - type(ESMF_State) , intent(inout) :: exportState - character(len=*) , intent(in) :: fldname - real(r8) , intent(in) :: lon(:) - real(r8) , intent(in) :: lat(:) - integer , intent(in) :: nf - integer, optional , intent(in) :: ungridded_index - integer , intent(out) :: rc - - ! local variables - integer :: i, ncomp - type(ESMF_Field) :: lfield - real(r8), pointer :: data1d(:) - real(r8), pointer :: data2d(:,:) - !-------------------------------------------------- - - rc = ESMF_SUCCESS - - call ESMF_StateGet(exportState, itemName=trim(fldname), field=lfield, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ncomp = 5 - if (present(ungridded_index)) then - call ESMF_FieldGet(lfield, farrayPtr=data2d, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - if (gridToFieldMap == 1) then - do i = 1,size(data2d, dim=1) - data2d(i,ungridded_index) = (nf*100) & - * cos (pi*lat(i)/180.0_R8) * cos (pi*lat(i)/180.0_R8) & - * sin (pi*lon(i)/180.0_R8) * sin (pi*lon(i)/180.0_R8) & - + (ncomp*10.0_R8) - enddo - else if (gridToFieldMap == 2) then - do i = 1,size(data2d, dim=2) - data2d(ungridded_index,i) = (nf*100) & - * cos (pi*lat(i)/180.0_R8) * cos (pi*lat(i)/180.0_R8) & - * sin (pi*lon(i)/180.0_R8) * sin (pi*lon(i)/180.0_R8) & - + (ncomp*10.0_R8) - end do - end if - else - call ESMF_FieldGet(lfield, farrayPtr=data1d, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - if (fldname == 'Sg_icemask' .or. fldname == 'Sg_icemask_coupled_fluxes' .or. fldname == 'Sg_ice_covered') then - data1d(:) = 1._r8 - else - do i = 1,size(data1d) - data1d(i) = (nf*100) & - * cos (pi*lat(i)/180.0_R8) * cos (pi*lat(i)/180.0_R8) & - * sin (pi*lon(i)/180.0_R8) * sin (pi*lon(i)/180.0_R8) & - + (ncomp*10.0_R8) - end do - end if - end if - - end subroutine field_setexport - - !=============================================================================== - subroutine ModelFinalize(gcomp, rc) - type(ESMF_GridComp) :: gcomp - integer, intent(out) :: rc - !------------------------------------------------------------------------------- - - rc = ESMF_SUCCESS - - if (mastertask) then - write(logunit,*) - write(logunit,*) 'xglc: end of main integration loop' - write(logunit,*) - end if - end subroutine ModelFinalize - -end module glc_comp_nuopc diff --git a/CIME/non_py/src/components/xcpl_comps_nuopc/xice/cime_config/buildlib b/CIME/non_py/src/components/xcpl_comps_nuopc/xice/cime_config/buildlib deleted file mode 120000 index 7766f77f5bc..00000000000 --- a/CIME/non_py/src/components/xcpl_comps_nuopc/xice/cime_config/buildlib +++ /dev/null @@ -1 +0,0 @@ -../../../../../../build_scripts/buildlib.internal_components \ No newline at end of file diff --git a/CIME/non_py/src/components/xcpl_comps_nuopc/xice/cime_config/buildlib_cmake b/CIME/non_py/src/components/xcpl_comps_nuopc/xice/cime_config/buildlib_cmake deleted file mode 120000 index 7766f77f5bc..00000000000 --- a/CIME/non_py/src/components/xcpl_comps_nuopc/xice/cime_config/buildlib_cmake +++ /dev/null @@ -1 +0,0 @@ -../../../../../../build_scripts/buildlib.internal_components \ No newline at end of file diff --git a/CIME/non_py/src/components/xcpl_comps_nuopc/xice/cime_config/buildnml b/CIME/non_py/src/components/xcpl_comps_nuopc/xice/cime_config/buildnml deleted file mode 100755 index 7d141edd619..00000000000 --- a/CIME/non_py/src/components/xcpl_comps_nuopc/xice/cime_config/buildnml +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env python3 - -""" -build data model library -""" - -import sys, os - -_CIMEROOT = os.path.join( - os.path.dirname(os.path.abspath(__file__)), "..", "..", "..", "..", ".." -) -sys.path.append(os.path.join(_CIMEROOT, "CIME", "Tools")) - -from standard_script_setup import * -from CIME.buildnml import build_xcpl_nml, parse_input -from CIME.case import Case - - -def buildnml(case, caseroot, compname): - if compname != "xice": - raise AttributeError - build_xcpl_nml(case, caseroot, compname) - - -def _main_func(): - caseroot = parse_input(sys.argv) - with Case(caseroot) as case: - buildnml(case, caseroot, "xice") - - -if __name__ == "__main__": - _main_func() diff --git a/CIME/non_py/src/components/xcpl_comps_nuopc/xice/cime_config/config_component.xml b/CIME/non_py/src/components/xcpl_comps_nuopc/xice/cime_config/config_component.xml deleted file mode 100644 index a3263a0eed6..00000000000 --- a/CIME/non_py/src/components/xcpl_comps_nuopc/xice/cime_config/config_component.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - Dead ice component - - - - char - xice - xice - case_comp - env_case.xml - Name of sea-ice component - - - - - ========================================= - XICE naming conventions in compset name - ========================================= - - - diff --git a/CIME/non_py/src/components/xcpl_comps_nuopc/xice/src/ice_comp_nuopc.F90 b/CIME/non_py/src/components/xcpl_comps_nuopc/xice/src/ice_comp_nuopc.F90 deleted file mode 100644 index 9185b8e532f..00000000000 --- a/CIME/non_py/src/components/xcpl_comps_nuopc/xice/src/ice_comp_nuopc.F90 +++ /dev/null @@ -1,552 +0,0 @@ -module ice_comp_nuopc - - !---------------------------------------------------------------------------- - ! This is the NUOPC cap for XICE - !---------------------------------------------------------------------------- - - use ESMF - use NUOPC , only : NUOPC_CompDerive, NUOPC_CompSetEntryPoint, NUOPC_CompSpecialize - use NUOPC , only : NUOPC_CompAttributeGet, NUOPC_Advertise - use NUOPC_Model , only : model_routine_SS => SetServices - use NUOPC_Model , only : model_label_Advance => label_Advance - use NUOPC_Model , only : model_label_SetRunClock => label_SetRunClock - use NUOPC_Model , only : model_label_Finalize => label_Finalize - use NUOPC_Model , only : NUOPC_ModelGet, SetVM - use shr_sys_mod , only : shr_sys_abort - use shr_kind_mod , only : r8=>shr_kind_r8, i8=>shr_kind_i8, cl=>shr_kind_cl, cs=>shr_kind_cs - use shr_log_mod , only : shr_log_getlogunit, shr_log_setlogunit - use dead_methods_mod , only : chkerr, state_setscalar, state_diagnose, alarmInit, memcheck - use dead_methods_mod , only : set_component_logging, get_component_instance, log_clock_advance - use dead_nuopc_mod , only : dead_read_inparms, ModelInitPhase, ModelSetRunClock - use dead_nuopc_mod , only : fld_list_add, fld_list_realize, fldsMax, fld_list_type - - implicit none - private ! except - - public :: SetServices - public :: SetVM - !-------------------------------------------------------------------------- - ! Private module data - !-------------------------------------------------------------------------- - - character(len=CL) :: flds_scalar_name = '' - integer :: flds_scalar_num = 0 - integer :: flds_scalar_index_nx = 0 - integer :: flds_scalar_index_ny = 0 - - integer :: fldsToIce_num = 0 - integer :: fldsFrIce_num = 0 - type (fld_list_type) :: fldsToIce(fldsMax) - type (fld_list_type) :: fldsFrIce(fldsMax) - integer, parameter :: gridTofieldMap = 2 ! ungridded dimension is innermost - - type(ESMF_Mesh) :: mesh - integer :: nxg ! global dim i-direction - integer :: nyg ! global dim j-direction - integer :: my_task ! my task in mpi communicator mpicom - integer :: inst_index ! number of current instance (ie. 1) - character(len=16) :: inst_suffix = "" ! char string associated with instance (ie. "_0001" or "") - integer :: logunit ! logging unit number - integer ,parameter :: master_task=0 ! task number of master task - logical :: mastertask - integer :: dbug = 0 - character(*),parameter :: modName = "(xice_comp_nuopc)" - character(*),parameter :: u_FILE_u = & - __FILE__ - -!=============================================================================== -contains -!=============================================================================== - - subroutine SetServices(gcomp, rc) - type(ESMF_GridComp) :: gcomp - integer, intent(out) :: rc - character(len=*),parameter :: subname=trim(modName)//':(SetServices) ' - - rc = ESMF_SUCCESS - call ESMF_LogWrite(subname//' called', ESMF_LOGMSG_INFO, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! the NUOPC gcomp component will register the generic methods - call NUOPC_CompDerive(gcomp, model_routine_SS, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! switching to IPD versions - call ESMF_GridCompSetEntryPoint(gcomp, ESMF_METHOD_INITIALIZE, & - userRoutine=ModelInitPhase, phase=0, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! set entry point for methods that require specific implementation - call NUOPC_CompSetEntryPoint(gcomp, ESMF_METHOD_INITIALIZE, phaseLabelList=(/"IPDv01p1"/), & - userRoutine=InitializeAdvertise, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call NUOPC_CompSetEntryPoint(gcomp, ESMF_METHOD_INITIALIZE, phaseLabelList=(/"IPDv01p3"/), & - userRoutine=InitializeRealize, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! attach specializing method(s) - call NUOPC_CompSpecialize(gcomp, specLabel=model_label_Advance, specRoutine=ModelAdvance, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call ESMF_MethodRemove(gcomp, label=model_label_SetRunClock, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - call NUOPC_CompSpecialize(gcomp, specLabel=model_label_SetRunClock, specRoutine=ModelSetRunClock, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call NUOPC_CompSpecialize(gcomp, specLabel=model_label_Finalize, specRoutine=ModelFinalize, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call ESMF_LogWrite(subname//' done', ESMF_LOGMSG_INFO, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - end subroutine SetServices - - !=============================================================================== - subroutine InitializeAdvertise(gcomp, importState, exportState, clock, rc) - - ! input/output variables - type(ESMF_GridComp) :: gcomp - type(ESMF_State) :: importState, exportState - type(ESMF_Clock) :: clock - integer, intent(out) :: rc - - ! local variables - type(ESMF_VM) :: vm - character(CL) :: cvalue - character(CS) :: stdname - integer :: n - integer :: lsize ! local array size - integer :: shrlogunit ! original log unit - character(len=CL) :: logmsg - logical :: isPresent, isSet - character(len=*),parameter :: subname=trim(modName)//':(InitializeAdvertise) ' - !------------------------------------------------------------------------------- - - rc = ESMF_SUCCESS - if (dbug > 5) call ESMF_LogWrite(subname//' called', ESMF_LOGMSG_INFO, rc=rc) - - call ESMF_GridCompGet(gcomp, vm=vm, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call ESMF_VMGet(vm, localpet=my_task, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - mastertask = my_task == master_task - - !---------------------------------------------------------------------------- - ! determine instance information - !---------------------------------------------------------------------------- - - call get_component_instance(gcomp, inst_suffix, inst_index, rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - !---------------------------------------------------------------------------- - ! set logunit and set shr logging to my log file - !---------------------------------------------------------------------------- - - call set_component_logging(gcomp, my_task==master_task, logunit, shrlogunit, rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - !---------------------------------------------------------------------------- - ! Initialize xice - !---------------------------------------------------------------------------- - - call dead_read_inparms('ice', inst_suffix, logunit, nxg, nyg) - - !-------------------------------- - ! advertise import and export fields - !-------------------------------- - - call NUOPC_CompAttributeGet(gcomp, name="ScalarFieldName", value=cvalue, isPresent=isPresent, isSet=isSet, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - if (isPresent .and. isSet) then - flds_scalar_name = trim(cvalue) - call ESMF_LogWrite(trim(subname)//' flds_scalar_name = '//trim(flds_scalar_name), ESMF_LOGMSG_INFO) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - else - call shr_sys_abort(subname//'Need to set attribute ScalarFieldName') - endif - - call NUOPC_CompAttributeGet(gcomp, name="ScalarFieldCount", value=cvalue, isPresent=isPresent, isSet=isSet, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - if (isPresent .and. isSet) then - read(cvalue, *) flds_scalar_num - write(logmsg,*) flds_scalar_num - call ESMF_LogWrite(trim(subname)//' flds_scalar_num = '//trim(logmsg), ESMF_LOGMSG_INFO) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - else - call shr_sys_abort(subname//'Need to set attribute ScalarFieldCount') - endif - - call NUOPC_CompAttributeGet(gcomp, name="ScalarFieldIdxGridNX", value=cvalue, isPresent=isPresent, isSet=isSet, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - if (isPresent .and. isSet) then - read(cvalue,*) flds_scalar_index_nx - write(logmsg,*) flds_scalar_index_nx - call ESMF_LogWrite(trim(subname)//' : flds_scalar_index_nx = '//trim(logmsg), ESMF_LOGMSG_INFO) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - else - call shr_sys_abort(subname//'Need to set attribute ScalarFieldIdxGridNX') - endif - - call NUOPC_CompAttributeGet(gcomp, name="ScalarFieldIdxGridNY", value=cvalue, isPresent=isPresent, isSet=isSet, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - if (isPresent .and. isSet) then - read(cvalue,*) flds_scalar_index_ny - write(logmsg,*) flds_scalar_index_ny - call ESMF_LogWrite(trim(subname)//' : flds_scalar_index_ny = '//trim(logmsg), ESMF_LOGMSG_INFO) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - else - call shr_sys_abort(subname//'Need to set attribute ScalarFieldIdxGridNY') - endif - - if (nxg /= 0 .and. nyg /= 0) then - - call fld_list_add(fldsFrIce_num, fldsFrIce, trim(flds_scalar_name)) - call fld_list_add(fldsFrIce_num, fldsFrIce, 'Si_imask' ) - call fld_list_add(fldsFrIce_num, fldsFrIce, 'Si_ifrac' ) - call fld_list_add(fldsFrIce_num, fldsFrIce, 'Si_t' ) - call fld_list_add(fldsFrIce_num, fldsFrIce, 'Si_tref' ) - call fld_list_add(fldsFrIce_num, fldsFrIce, 'Si_qref' ) - call fld_list_add(fldsFrIce_num, fldsFrIce, 'Si_snowh' ) - call fld_list_add(fldsFrIce_num, fldsFrIce, 'Si_u10' ) - call fld_list_add(fldsFrIce_num, fldsFrIce, 'Si_avsdr' ) - call fld_list_add(fldsFrIce_num, fldsFrIce, 'Si_anidr' ) - call fld_list_add(fldsFrIce_num, fldsFrIce, 'Si_avsdf' ) - call fld_list_add(fldsFrIce_num, fldsFrIce, 'Si_anidf' ) - call fld_list_add(fldsFrIce_num, fldsFrIce, 'Faii_taux' ) - call fld_list_add(fldsFrIce_num, fldsFrIce, 'Faii_tauy' ) - call fld_list_add(fldsFrIce_num, fldsFrIce, 'Faii_lat' ) - call fld_list_add(fldsFrIce_num, fldsFrIce, 'Faii_sen' ) - call fld_list_add(fldsFrIce_num, fldsFrIce, 'Faii_lwup' ) - call fld_list_add(fldsFrIce_num, fldsFrIce, 'Faii_evap' ) - call fld_list_add(fldsFrIce_num, fldsFrIce, 'Faii_swnet' ) - call fld_list_add(fldsFrIce_num, fldsFrIce, 'Fioi_melth' ) - call fld_list_add(fldsFrIce_num, fldsFrIce, 'Fioi_swpen' ) - call fld_list_add(fldsFrIce_num, fldsFrIce, 'Fioi_meltw' ) - call fld_list_add(fldsFrIce_num, fldsFrIce, 'Fioi_salt' ) - call fld_list_add(fldsFrIce_num, fldsFrIce, 'Fioi_taux' ) - call fld_list_add(fldsFrIce_num, fldsFrIce, 'Fioi_tauy' ) - call fld_list_add(fldsFrIce_num, fldsFrIce, 'Fioi_bcpho' ) - call fld_list_add(fldsFrIce_num, fldsFrIce, 'Fioi_bcphi' ) - call fld_list_add(fldsFrIce_num, fldsFrIce, 'Fioi_flxdst' ) - - call fld_list_add(fldsToIce_num, fldsToIce, trim(flds_scalar_name)) - call fld_list_add(fldsToIce_num, fldsToIce, 'So_dhdx' ) - call fld_list_add(fldsToIce_num, fldsToIce, 'So_dhdy' ) - call fld_list_add(fldsToIce_num, fldsToIce, 'So_t' ) - call fld_list_add(fldsToIce_num, fldsToIce, 'So_s' ) - call fld_list_add(fldsToIce_num, fldsToIce, 'So_u' ) - call fld_list_add(fldsToIce_num, fldsToIce, 'So_v' ) - call fld_list_add(fldsToIce_num, fldsToIce, 'Fioo_q' ) - call fld_list_add(fldsToIce_num, fldsToIce, 'Sa_z' ) - call fld_list_add(fldsToIce_num, fldsToIce, 'Sa_u' ) - call fld_list_add(fldsToIce_num, fldsToIce, 'Sa_v' ) - call fld_list_add(fldsToIce_num, fldsToIce, 'Sa_ptem' ) - call fld_list_add(fldsToIce_num, fldsToIce, 'Sa_shum' ) - call fld_list_add(fldsToIce_num, fldsToIce, 'Sa_dens' ) - call fld_list_add(fldsToIce_num, fldsToIce, 'Sa_tbot' ) - call fld_list_add(fldsToIce_num, fldsToIce, 'Faxa_swvdr' ) - call fld_list_add(fldsToIce_num, fldsToIce, 'Faxa_swndr' ) - call fld_list_add(fldsToIce_num, fldsToIce, 'Faxa_swvdf' ) - call fld_list_add(fldsToIce_num, fldsToIce, 'Faxa_swndf' ) - call fld_list_add(fldsToIce_num, fldsToIce, 'Faxa_lwdn' ) - call fld_list_add(fldsToIce_num, fldsToIce, 'Faxa_rain' ) - call fld_list_add(fldsToIce_num, fldsToIce, 'Faxa_snow' ) - call fld_list_add(fldsToIce_num, fldsToIce, 'Faxa_bcph' , ungridded_lbound=1, ungridded_ubound=3) - call fld_list_add(fldsToIce_num, fldsToIce, 'Faxa_ocph' , ungridded_lbound=1, ungridded_ubound=3) - call fld_list_add(fldsToIce_num, fldsToIce, 'Faxa_dstwet', ungridded_lbound=1, ungridded_ubound=4) - call fld_list_add(fldsToIce_num, fldsToIce, 'Faxa_dstdry', ungridded_lbound=1, ungridded_ubound=4) - - do n = 1,fldsFrIce_num - if(mastertask) write(logunit,*)'Advertising From Xice ',trim(fldsFrIce(n)%stdname) - call NUOPC_Advertise(exportState, standardName=fldsFrIce(n)%stdname, & - TransferOfferGeomObject='will provide', rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - enddo - - do n = 1,fldsToIce_num - if(mastertask) write(logunit,*)'Advertising To Xice ',trim(fldsToIce(n)%stdname) - call NUOPC_Advertise(importState, standardName=fldsToIce(n)%stdname, & - TransferOfferGeomObject='will provide', rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - end do - end if - - - if (dbug > 5) call ESMF_LogWrite(subname//' done', ESMF_LOGMSG_INFO, rc=rc) - - !---------------------------------------------------------------------------- - ! Reset shr logging to original values - !---------------------------------------------------------------------------- - - call shr_log_setLogUnit (shrlogunit) - - end subroutine InitializeAdvertise - - !=============================================================================== - subroutine InitializeRealize(gcomp, importState, exportState, clock, rc) - type(ESMF_GridComp) :: gcomp - type(ESMF_State) :: importState, exportState - type(ESMF_Clock) :: clock - integer, intent(out) :: rc - - ! local variables - integer :: shrlogunit ! original log unit - integer :: n - character(ESMF_MAXSTR) :: cvalue ! config data - character(len=*),parameter :: subname=trim(modName)//':(InitializeRealize) ' - !------------------------------------------------------------------------------- - - rc = ESMF_SUCCESS - - !---------------------------------------------------------------------------- - ! Reset shr logging to my log file - !---------------------------------------------------------------------------- - - call shr_log_getLogUnit (shrlogunit) - call shr_log_setLogUnit (logUnit) - - !-------------------------------- - ! generate the mesh - !-------------------------------- - - call NUOPC_CompAttributeGet(gcomp, name='mesh_ice', value=cvalue, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - - mesh = ESMF_MeshCreate(filename=trim(cvalue), fileformat=ESMF_FILEFORMAT_ESMFMESH, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - - !-------------------------------- - ! realize the actively coupled fields, now that a mesh is established - ! NUOPC_Realize "realizes" a previously advertised field in the importState and exportState - ! by replacing the advertised fields with the newly created fields of the same name. - !-------------------------------- - - call fld_list_realize( & - state=ExportState, & - fldlist=fldsFrIce, & - numflds=fldsFrIce_num, & - flds_scalar_name=flds_scalar_name, & - flds_scalar_num=flds_scalar_num, & - tag=subname//':diceExport',& - mesh=mesh, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call fld_list_realize( & - state=importState, & - fldList=fldsToIce, & - numflds=fldsToIce_num, & - flds_scalar_name=flds_scalar_name, & - flds_scalar_num=flds_scalar_num, & - tag=subname//':diceImport',& - mesh=mesh, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - !-------------------------------- - ! Pack export state - !-------------------------------- - - call State_SetExport(exportState, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call State_SetScalar(dble(nxg),flds_scalar_index_nx, exportState, & - flds_scalar_name, flds_scalar_num, rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call State_SetScalar(dble(nyg),flds_scalar_index_ny, exportState, & - flds_scalar_name, flds_scalar_num, rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - !-------------------------------- - ! diagnostics - !-------------------------------- - - if (dbug > 1) then - call State_diagnose(exportState,subname//':ES',rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - endif - - call shr_log_setLogUnit (shrlogunit) - - end subroutine InitializeRealize - - !=============================================================================== - subroutine ModelAdvance(gcomp, rc) - - ! input/output variables - type(ESMF_GridComp) :: gcomp - integer, intent(out) :: rc - - ! local variables - type(ESMF_Clock) :: clock - type(ESMF_State) :: exportState - integer :: shrlogunit ! original log unit - character(len=*),parameter :: subname=trim(modName)//':(ModelAdvance) ' - !------------------------------------------------------------------------------- - - rc = ESMF_SUCCESS - call ESMF_LogWrite(subname//' called', ESMF_LOGMSG_INFO, rc=rc) - call memcheck(subname, 3, mastertask) - - call shr_log_getLogUnit (shrlogunit) - call shr_log_setLogUnit (logunit) - - !-------------------------------- - ! Pack export state - !-------------------------------- - - call NUOPC_ModelGet(gcomp, modelClock=clock, exportState=exportState, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call state_setexport(exportState, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - !-------------------------------- - ! diagnostics - !-------------------------------- - - if (dbug > 1) then - call State_diagnose(exportState,subname//':ES',rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - if (my_task == master_task) then - call log_clock_advance(clock, 'XICE', logunit, rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - endif - endif - - call shr_log_setLogUnit (shrlogunit) - - call ESMF_LogWrite(subname//' done', ESMF_LOGMSG_INFO, rc=rc) - - end subroutine ModelAdvance - - !=============================================================================== - subroutine state_setexport(exportState, rc) - - ! input/output variables - type(ESMF_State) , intent(inout) :: exportState - integer, intent(out) :: rc - - ! local variables - integer :: n, nf, nind - real(r8), pointer :: lat(:) - real(r8), pointer :: lon(:) - integer :: spatialDim - integer :: numOwnedElements - real(R8), pointer :: ownedElemCoords(:) - !-------------------------------------------------- - - rc = ESMF_SUCCESS - - call ESMF_MeshGet(mesh, spatialDim=spatialDim, numOwnedElements=numOwnedElements, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - allocate(ownedElemCoords(spatialDim*numOwnedElements)) - call ESMF_MeshGet(mesh, ownedElemCoords=ownedElemCoords) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - - allocate(lon(numownedElements)) - allocate(lat(numownedElements)) - do n = 1,numownedElements - lon(n) = ownedElemCoords(2*n-1) - lat(n) = ownedElemCoords(2*n) - end do - - ! Start from index 2 in order to skip the scalar field - do nf = 2,fldsFrIce_num - if (fldsFrIce(nf)%ungridded_ubound == 0) then - call field_setexport(exportState, trim(fldsFrIce(nf)%stdname), lon, lat, nf=nf, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - else - do nind = 1,fldsFrIce(nf)%ungridded_ubound - call field_setexport(exportState, trim(fldsFrIce(nf)%stdname), lon, lat, nf=nf+nind-1, & - ungridded_index=nind, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - end do - end if - end do - - deallocate(lon) - deallocate(lat) - - end subroutine state_setexport - - !=============================================================================== - subroutine field_setexport(exportState, fldname, lon, lat, nf, ungridded_index, rc) - - use shr_const_mod , only : pi=>shr_const_pi - - ! intput/otuput variables - type(ESMF_State) , intent(inout) :: exportState - character(len=*) , intent(in) :: fldname - real(r8) , intent(in) :: lon(:) - real(r8) , intent(in) :: lat(:) - integer , intent(in) :: nf - integer, optional , intent(in) :: ungridded_index - integer , intent(out) :: rc - - ! local variables - integer :: i, ncomp - type(ESMF_Field) :: lfield - real(r8), pointer :: data1d(:) - real(r8), pointer :: data2d(:,:) - !-------------------------------------------------- - - rc = ESMF_SUCCESS - - call ESMF_StateGet(exportState, itemName=trim(fldname), field=lfield, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ncomp = 3 - if (present(ungridded_index)) then - call ESMF_FieldGet(lfield, farrayPtr=data2d, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - if (gridToFieldMap == 1) then - do i = 1,size(data2d, dim=1) - data2d(i,ungridded_index) = (nf*100) * cos(pi*lat(i)/180.0_R8) * & - sin((pi*lon(i)/180.0_R8) - (ncomp-1)*(pi/3.0_R8) ) + (ncomp*10.0_R8) - end do - else if (gridToFieldMap == 2) then - do i = 1,size(data2d, dim=2) - data2d(ungridded_index,i) = (nf*100) * cos(pi*lat(i)/180.0_R8) * & - sin((pi*lon(i)/180.0_R8) - (ncomp-1)*(pi/3.0_R8) ) + (ncomp*10.0_R8) - end do - end if - else - call ESMF_FieldGet(lfield, farrayPtr=data1d, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - do i = 1,size(data1d) - data1d(i) = (nf*100) * cos(pi*lat(i)/180.0_R8) * & - sin((pi*lon(i)/180.0_R8) - (ncomp-1)*(pi/3.0_R8) ) + (ncomp*10.0_R8) - end do - ! Reset some fields - if (fldname == 'Si_ifrac') then - do i = 1,size(data1d) - data1d(i) = min(1.0_R8,max(0.0_R8,data1d(i))) - end do - else if (fldname == 'Si_imask') then - do i = 1,size(data1d) - data1d(i) = float(nint(min(1.0_R8,max(0.0_R8,data1d(i))))) - end do - end if - end if - - end subroutine field_setexport - - !=============================================================================== - subroutine ModelFinalize(gcomp, rc) - type(ESMF_GridComp) :: gcomp - integer, intent(out) :: rc - !------------------------------------------------------------------------------- - - rc = ESMF_SUCCESS - - if (mastertask) then - write(logunit,*) - write(logunit,*) 'xice: end of main integration loop' - write(logunit,*) - end if - end subroutine ModelFinalize - -end module ice_comp_nuopc diff --git a/CIME/non_py/src/components/xcpl_comps_nuopc/xlnd/cime_config/buildlib b/CIME/non_py/src/components/xcpl_comps_nuopc/xlnd/cime_config/buildlib deleted file mode 120000 index 7766f77f5bc..00000000000 --- a/CIME/non_py/src/components/xcpl_comps_nuopc/xlnd/cime_config/buildlib +++ /dev/null @@ -1 +0,0 @@ -../../../../../../build_scripts/buildlib.internal_components \ No newline at end of file diff --git a/CIME/non_py/src/components/xcpl_comps_nuopc/xlnd/cime_config/buildlib_cmake b/CIME/non_py/src/components/xcpl_comps_nuopc/xlnd/cime_config/buildlib_cmake deleted file mode 120000 index 7766f77f5bc..00000000000 --- a/CIME/non_py/src/components/xcpl_comps_nuopc/xlnd/cime_config/buildlib_cmake +++ /dev/null @@ -1 +0,0 @@ -../../../../../../build_scripts/buildlib.internal_components \ No newline at end of file diff --git a/CIME/non_py/src/components/xcpl_comps_nuopc/xlnd/cime_config/buildnml b/CIME/non_py/src/components/xcpl_comps_nuopc/xlnd/cime_config/buildnml deleted file mode 100755 index 72e822771b4..00000000000 --- a/CIME/non_py/src/components/xcpl_comps_nuopc/xlnd/cime_config/buildnml +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env python3 - -""" -build data model library -""" - -import sys, os - -_CIMEROOT = os.path.join( - os.path.dirname(os.path.abspath(__file__)), "..", "..", "..", "..", ".." -) -sys.path.append(os.path.join(_CIMEROOT, "CIME", "Tools")) - -from standard_script_setup import * -from CIME.buildnml import build_xcpl_nml, parse_input -from CIME.case import Case - - -def buildnml(case, caseroot, compname): - if compname != "xlnd": - raise AttributeError - build_xcpl_nml(case, caseroot, compname) - - -def _main_func(): - caseroot = parse_input(sys.argv) - with Case(caseroot) as case: - buildnml(case, caseroot, "xlnd") - - -if __name__ == "__main__": - _main_func() diff --git a/CIME/non_py/src/components/xcpl_comps_nuopc/xlnd/cime_config/config_component.xml b/CIME/non_py/src/components/xcpl_comps_nuopc/xlnd/cime_config/config_component.xml deleted file mode 100644 index 8179d03108b..00000000000 --- a/CIME/non_py/src/components/xcpl_comps_nuopc/xlnd/cime_config/config_component.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - Dead land component - - - - - char - xlnd - xlnd - case_comp - env_case.xml - Name of land component - - - - ========================================= - XLND naming conventions in compset name - ========================================= - - - diff --git a/CIME/non_py/src/components/xcpl_comps_nuopc/xlnd/src/lnd_comp_nuopc.F90 b/CIME/non_py/src/components/xcpl_comps_nuopc/xlnd/src/lnd_comp_nuopc.F90 deleted file mode 100644 index a43215939ad..00000000000 --- a/CIME/non_py/src/components/xcpl_comps_nuopc/xlnd/src/lnd_comp_nuopc.F90 +++ /dev/null @@ -1,564 +0,0 @@ -module lnd_comp_nuopc - - !---------------------------------------------------------------------------- - ! This is the NUOPC cap for XLND - !---------------------------------------------------------------------------- - - use ESMF - use NUOPC , only : NUOPC_CompDerive, NUOPC_CompSetEntryPoint, NUOPC_CompSpecialize - use NUOPC , only : NUOPC_CompAttributeGet, NUOPC_Advertise - use NUOPC_Model , only : model_routine_SS => SetServices - use NUOPC_Model , only : model_label_Advance => label_Advance - use NUOPC_Model , only : model_label_SetRunClock => label_SetRunClock - use NUOPC_Model , only : model_label_Finalize => label_Finalize - use NUOPC_Model , only : NUOPC_ModelGet, SetVM - use shr_sys_mod , only : shr_sys_abort - use shr_kind_mod , only : r8=>shr_kind_r8, i8=>shr_kind_i8, cl=>shr_kind_cl, cs=>shr_kind_cs - use shr_log_mod , only : shr_log_getlogunit, shr_log_setlogunit - use dead_methods_mod , only : chkerr, state_setscalar, state_diagnose, alarmInit, memcheck - use dead_methods_mod , only : set_component_logging, get_component_instance, log_clock_advance - use dead_nuopc_mod , only : dead_read_inparms, ModelInitPhase, ModelSetRunClock - use dead_nuopc_mod , only : fld_list_add, fld_list_realize, fldsMax, fld_list_type - - implicit none - private ! except - - public :: SetServices - public :: SetVM - !-------------------------------------------------------------------------- - ! Private module data - !-------------------------------------------------------------------------- - - character(len=CL) :: flds_scalar_name = '' - integer :: flds_scalar_num = 0 - integer :: flds_scalar_index_nx = 0 - integer :: flds_scalar_index_ny = 0 - integer :: flds_scalar_index_nextsw_cday = 0._r8 - - integer :: fldsToLnd_num = 0 - integer :: fldsFrLnd_num = 0 - type (fld_list_type) :: fldsToLnd(fldsMax) - type (fld_list_type) :: fldsFrLnd(fldsMax) - integer, parameter :: gridTofieldMap = 2 ! ungridded dimension is innermost - integer :: glc_nec - - type(ESMF_Mesh) :: mesh - integer :: nxg ! global dim i-direction - integer :: nyg ! global dim j-direction - integer :: my_task ! my task in mpi communicator mpicom - integer :: inst_index ! number of current instance (ie. 1) - character(len=16) :: inst_suffix = "" ! char string associated with instance (ie. "_0001" or "") - integer :: logunit ! logging unit number - integer ,parameter :: master_task=0 ! task number of master task - logical :: mastertask - integer :: dbug = 1 - character(*),parameter :: modName = "(xlnd_comp_nuopc)" - character(*),parameter :: u_FILE_u = & - __FILE__ - -!=============================================================================== -contains -!=============================================================================== - - subroutine SetServices(gcomp, rc) - - type(ESMF_GridComp) :: gcomp - integer, intent(out) :: rc - character(len=*),parameter :: subname=trim(modName)//':(SetServices) ' - - rc = ESMF_SUCCESS - call ESMF_LogWrite(subname//' called', ESMF_LOGMSG_INFO, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! the NUOPC gcomp component will register the generic methods - call NUOPC_CompDerive(gcomp, model_routine_SS, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! switching to IPD versions - call ESMF_GridCompSetEntryPoint(gcomp, ESMF_METHOD_INITIALIZE, & - userRoutine=ModelInitPhase, phase=0, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! set entry point for methods that require specific implementation - call NUOPC_CompSetEntryPoint(gcomp, ESMF_METHOD_INITIALIZE, phaseLabelList=(/"IPDv01p1"/), & - userRoutine=InitializeAdvertise, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call NUOPC_CompSetEntryPoint(gcomp, ESMF_METHOD_INITIALIZE, phaseLabelList=(/"IPDv01p3"/), & - userRoutine=InitializeRealize, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! attach specializing method(s) - call NUOPC_CompSpecialize(gcomp, specLabel=model_label_Advance, specRoutine=ModelAdvance, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call ESMF_MethodRemove(gcomp, label=model_label_SetRunClock, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - call NUOPC_CompSpecialize(gcomp, specLabel=model_label_SetRunClock, specRoutine=ModelSetRunClock, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call NUOPC_CompSpecialize(gcomp, specLabel=model_label_Finalize, specRoutine=ModelFinalize, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call ESMF_LogWrite(subname//' done', ESMF_LOGMSG_INFO, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - end subroutine SetServices - - !=============================================================================== - - subroutine InitializeAdvertise(gcomp, importState, exportState, clock, rc) - - ! input/output variables - type(ESMF_GridComp) :: gcomp - type(ESMF_State) :: importState, exportState - type(ESMF_Clock) :: clock - integer, intent(out) :: rc - - ! local variables - type(ESMF_VM) :: vm - character(CS) :: stdname - integer :: n - integer :: lsize ! local array size - integer :: shrlogunit ! original log unit - character(CL) :: cvalue - character(CL) :: logmsg - logical :: isPresent, isSet - character(len=*),parameter :: subname=trim(modName)//':(InitializeAdvertise) ' - !------------------------------------------------------------------------------- - - rc = ESMF_SUCCESS - if (dbug > 5) call ESMF_LogWrite(subname//' called', ESMF_LOGMSG_INFO, rc=rc) - - call ESMF_GridCompGet(gcomp, vm=vm, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call ESMF_VMGet(vm, localpet=my_task, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - mastertask = (my_task == master_task) - - !---------------------------------------------------------------------------- - ! determine instance information - !---------------------------------------------------------------------------- - - call get_component_instance(gcomp, inst_suffix, inst_index, rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - !---------------------------------------------------------------------------- - ! set logunit and set shr logging to my log file - !---------------------------------------------------------------------------- - - call set_component_logging(gcomp, mastertask, logunit, shrlogunit, rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - !---------------------------------------------------------------------------- - ! Initialize xlnd - !---------------------------------------------------------------------------- - - call dead_read_inparms('lnd', inst_suffix, logunit, nxg, nyg) - - !-------------------------------- - ! advertise import and export fields - !-------------------------------- - - call NUOPC_CompAttributeGet(gcomp, name="ScalarFieldName", value=cvalue, isPresent=isPresent, isSet=isSet, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - if (isPresent .and. isSet) then - flds_scalar_name = trim(cvalue) - call ESMF_LogWrite(trim(subname)//' flds_scalar_name = '//trim(flds_scalar_name), ESMF_LOGMSG_INFO) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - else - call shr_sys_abort(subname//'Need to set attribute ScalarFieldName') - endif - - call NUOPC_CompAttributeGet(gcomp, name="ScalarFieldCount", value=cvalue, isPresent=isPresent, isSet=isSet, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - if (isPresent .and. isSet) then - read(cvalue, *) flds_scalar_num - write(logmsg,*) flds_scalar_num - call ESMF_LogWrite(trim(subname)//' flds_scalar_num = '//trim(logmsg), ESMF_LOGMSG_INFO) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - else - call shr_sys_abort(subname//'Need to set attribute ScalarFieldCount') - endif - - call NUOPC_CompAttributeGet(gcomp, name="ScalarFieldIdxGridNX", value=cvalue, isPresent=isPresent, isSet=isSet, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - if (isPresent .and. isSet) then - read(cvalue,*) flds_scalar_index_nx - write(logmsg,*) flds_scalar_index_nx - call ESMF_LogWrite(trim(subname)//' : flds_scalar_index_nx = '//trim(logmsg), ESMF_LOGMSG_INFO) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - else - call shr_sys_abort(subname//'Need to set attribute ScalarFieldIdxGridNX') - endif - - call NUOPC_CompAttributeGet(gcomp, name="ScalarFieldIdxGridNY", value=cvalue, isPresent=isPresent, isSet=isSet, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - if (isPresent .and. isSet) then - read(cvalue,*) flds_scalar_index_ny - write(logmsg,*) flds_scalar_index_ny - call ESMF_LogWrite(trim(subname)//' : flds_scalar_index_ny = '//trim(logmsg), ESMF_LOGMSG_INFO) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - else - call shr_sys_abort(subname//'Need to set attribute ScalarFieldIdxGridNY') - end if - - if (nxg /= 0 .and. nyg /= 0) then - - call NUOPC_CompAttributeGet(gcomp, name='glc_nec', value=cvalue, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - read(cvalue,*) glc_nec - call ESMF_LogWrite('glc_nec = '// trim(cvalue), ESMF_LOGMSG_INFO) - - call fld_list_add(fldsFrLnd_num, fldsFrlnd, trim(flds_scalar_name)) - call fld_list_add(fldsFrLnd_num, fldsFrlnd, 'Sl_lfrin' ) - call fld_list_add(fldsFrLnd_num, fldsFrlnd, 'Sl_t' ) - call fld_list_add(fldsFrLnd_num, fldsFrlnd, 'Sl_tref' ) - call fld_list_add(fldsFrLnd_num, fldsFrlnd, 'Sl_qref' ) - call fld_list_add(fldsFrLnd_num, fldsFrlnd, 'Sl_avsdr' ) - call fld_list_add(fldsFrLnd_num, fldsFrlnd, 'Sl_anidr' ) - call fld_list_add(fldsFrLnd_num, fldsFrlnd, 'Sl_avsdf' ) - call fld_list_add(fldsFrLnd_num, fldsFrlnd, 'Sl_anidf' ) - call fld_list_add(fldsFrLnd_num, fldsFrlnd, 'Sl_snowh' ) - call fld_list_add(fldsFrLnd_num, fldsFrlnd, 'Sl_u10' ) - call fld_list_add(fldsFrLnd_num, fldsFrlnd, 'Sl_fv' ) - call fld_list_add(fldsFrLnd_num, fldsFrlnd, 'Sl_ram1' ) - call fld_list_add(fldsFrLnd_num, fldsFrlnd, 'Flrl_rofsur' ) - call fld_list_add(fldsFrLnd_num, fldsFrlnd, 'Flrl_rofgwl' ) - call fld_list_add(fldsFrLnd_num, fldsFrlnd, 'Flrl_rofsub' ) - call fld_list_add(fldsFrLnd_num, fldsFrlnd, 'Flrl_rofi' ) - call fld_list_add(fldsFrLnd_num, fldsFrlnd, 'Flrl_irrig' ) - call fld_list_add(fldsFrLnd_num, fldsFrlnd, 'Fall_taux' ) - call fld_list_add(fldsFrLnd_num, fldsFrlnd, 'Fall_tauy' ) - call fld_list_add(fldsFrLnd_num, fldsFrlnd, 'Fall_lat' ) - call fld_list_add(fldsFrLnd_num, fldsFrlnd, 'Fall_sen' ) - call fld_list_add(fldsFrLnd_num, fldsFrlnd, 'Fall_lwup' ) - call fld_list_add(fldsFrLnd_num, fldsFrlnd, 'Fall_evap' ) - call fld_list_add(fldsFrLnd_num, fldsFrlnd, 'Fall_swnet' ) - call fld_list_add(fldsFrLnd_num, fldsFrlnd, 'Fall_flxdst' , ungridded_lbound=1, ungridded_ubound=4) - call fld_list_add(fldsFrLnd_num, fldsFrlnd, 'Flgl_qice_elev', ungridded_lbound=1, ungridded_ubound=glc_nec+1) - call fld_list_add(fldsFrLnd_num, fldsFrlnd, 'Sl_tsrf_elev' , ungridded_lbound=1, ungridded_ubound=glc_nec+1) - call fld_list_add(fldsFrLnd_num, fldsFrlnd, 'Sl_topo_elev' , ungridded_lbound=1, ungridded_ubound=glc_nec+1) - - call fld_list_add(fldsToLnd_num, fldsToLnd, trim(flds_scalar_name)) - call fld_list_add(fldsToLnd_num, fldsToLnd, 'Sa_z' ) - call fld_list_add(fldsToLnd_num, fldsToLnd, 'Sa_topo' ) - call fld_list_add(fldsToLnd_num, fldsToLnd, 'Sa_u' ) - call fld_list_add(fldsToLnd_num, fldsToLnd, 'Sa_v' ) - call fld_list_add(fldsToLnd_num, fldsToLnd, 'Sa_ptem' ) - call fld_list_add(fldsToLnd_num, fldsToLnd, 'Sa_pbot' ) - call fld_list_add(fldsToLnd_num, fldsToLnd, 'Sa_tbot' ) - call fld_list_add(fldsToLnd_num, fldsToLnd, 'Sa_shum' ) - call fld_list_add(fldsToLnd_num, fldsToLnd, 'Flrr_volr' ) - call fld_list_add(fldsToLnd_num, fldsToLnd, 'Flrr_volrmch' ) - call fld_list_add(fldsToLnd_num, fldsToLnd, 'Faxa_lwdn' ) - call fld_list_add(fldsToLnd_num, fldsToLnd, 'Faxa_rainc' ) - call fld_list_add(fldsToLnd_num, fldsToLnd, 'Faxa_rainl' ) - call fld_list_add(fldsToLnd_num, fldsToLnd, 'Faxa_snowc' ) - call fld_list_add(fldsToLnd_num, fldsToLnd, 'Faxa_snowl' ) - call fld_list_add(fldsToLnd_num, fldsToLnd, 'Faxa_swndr' ) - call fld_list_add(fldsToLnd_num, fldsToLnd, 'Faxa_swvdr' ) - call fld_list_add(fldsToLnd_num, fldsToLnd, 'Faxa_swndf' ) - call fld_list_add(fldsToLnd_num, fldsToLnd, 'Faxa_swvdf' ) - call fld_list_add(fldsTolnd_num, fldsTolnd, 'Faxa_bcph' , ungridded_lbound=1, ungridded_ubound=3) - call fld_list_add(fldsTolnd_num, fldsTolnd, 'Faxa_ocph' , ungridded_lbound=1, ungridded_ubound=3) - call fld_list_add(fldsTolnd_num, fldsTolnd, 'Faxa_dstwet' , ungridded_lbound=1, ungridded_ubound=4) - call fld_list_add(fldsTolnd_num, fldsTolnd, 'Faxa_dstdry' , ungridded_lbound=1, ungridded_ubound=4) - call fld_list_add(fldsToLnd_num, fldsTolnd, 'Sg_topo_elev' , ungridded_lbound=1, ungridded_ubound=glc_nec+1) - call fld_list_add(fldsToLnd_num, fldsTolnd, 'Sg_ice_covered_elev' , ungridded_lbound=1, ungridded_ubound=glc_nec+1) - call fld_list_add(fldsToLnd_num, fldsTolnd, 'Flgg_hflx_elev' , ungridded_lbound=1, ungridded_ubound=glc_nec+1) - call fld_list_add(fldsToLnd_num, fldsTolnd, 'Sg_icemask') - call fld_list_add(fldsToLnd_num, fldsTolnd, 'Sg_icemask_coupled_fluxes') - - do n = 1,fldsFrLnd_num - if (mastertask) write(logunit,*)'Advertising From Xlnd ',trim(fldsFrLnd(n)%stdname) - call NUOPC_Advertise(exportState, standardName=fldsFrLnd(n)%stdname, & - TransferOfferGeomObject='will provide', rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - enddo - - do n = 1,fldsToLnd_num - if(mastertask) write(logunit,*)'Advertising To Xlnd',trim(fldsToLnd(n)%stdname) - call NUOPC_Advertise(importState, standardName=fldsToLnd(n)%stdname, & - TransferOfferGeomObject='will provide', rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - enddo - - end if - - !---------------------------------------------------------------------------- - ! Reset shr logging to original values - !---------------------------------------------------------------------------- - - call shr_log_setLogUnit (shrlogunit) - - end subroutine InitializeAdvertise - - !=============================================================================== - subroutine InitializeRealize(gcomp, importState, exportState, clock, rc) - - ! intput/output variables - type(ESMF_GridComp) :: gcomp - type(ESMF_State) :: importState, exportState - type(ESMF_Clock) :: clock - integer, intent(out) :: rc - - ! local variables - integer :: shrlogunit ! original log unit - integer :: n - character(ESMF_MAXSTR) :: cvalue ! config data - character(len=*),parameter :: subname=trim(modName)//':(InitializeRealize) ' - !------------------------------------------------------------------------------- - - rc = ESMF_SUCCESS - if (dbug > 5) call ESMF_LogWrite(subname//' called', ESMF_LOGMSG_INFO, rc=rc) - - !---------------------------------------------------------------------------- - ! Reset shr logging to my log file - !---------------------------------------------------------------------------- - - call shr_log_getLogUnit (shrlogunit) - call shr_log_setLogUnit (logUnit) - - !-------------------------------- - ! generate the mesh - !-------------------------------- - - call NUOPC_CompAttributeGet(gcomp, name='mesh_lnd', value=cvalue, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - - mesh = ESMF_MeshCreate(filename=trim(cvalue), fileformat=ESMF_FILEFORMAT_ESMFMESH, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - - !-------------------------------- - ! realize the actively coupled fields, now that a mesh is established - ! NUOPC_Realize "realizes" a previously advertised field in the importState and exportState - ! by replacing the advertised fields with the newly created fields of the same name. - !-------------------------------- - - call fld_list_realize( & - state=ExportState, & - fldlist=fldsFrLnd, & - numflds=fldsFrLnd_num, & - flds_scalar_name=flds_scalar_name, & - flds_scalar_num=flds_scalar_num, & - tag=subname//':dlndExport',& - mesh=mesh, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call fld_list_realize( & - state=importState, & - fldList=fldsToLnd, & - numflds=fldsToLnd_num, & - flds_scalar_name=flds_scalar_name, & - flds_scalar_num=flds_scalar_num, & - tag=subname//':dlndImport',& - mesh=mesh, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - !-------------------------------- - ! Pack export state - !-------------------------------- - - call state_setexport(exportState, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call State_SetScalar(dble(nxg),flds_scalar_index_nx, exportState, & - flds_scalar_name, flds_scalar_num, rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call State_SetScalar(dble(nyg),flds_scalar_index_ny, exportState, & - flds_scalar_name, flds_scalar_num, rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - !-------------------------------- - ! diagnostics - !-------------------------------- - - if (dbug > 1) then - call state_diagnose(exportState,subname//':ES',rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - endif - - call shr_log_setLogUnit (shrlogunit) - - if (dbug > 5) call ESMF_LogWrite(subname//' done', ESMF_LOGMSG_INFO, rc=rc) - - end subroutine InitializeRealize - - !=============================================================================== - subroutine ModelAdvance(gcomp, rc) - - ! input/output variables - type(ESMF_GridComp) :: gcomp - integer, intent(out) :: rc - - ! local variables - type(ESMF_Clock) :: clock - type(ESMF_State) :: exportState - integer :: shrlogunit ! original log unit - character(len=*),parameter :: subname=trim(modName)//':(ModelAdvance) ' - !------------------------------------------------------------------------------- - - rc = ESMF_SUCCESS - call ESMF_LogWrite(subname//' called', ESMF_LOGMSG_INFO, rc=rc) - call memcheck(subname, 3, mastertask) - - call shr_log_getLogUnit (shrlogunit) - call shr_log_setLogUnit (logunit) - - !-------------------------------- - ! Pack export state - !-------------------------------- - - call NUOPC_ModelGet(gcomp, modelClock=clock, exportState=exportState, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call state_setexport(exportState, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - !-------------------------------- - ! diagnostics - !-------------------------------- - - if (dbug > 1) then - call state_diagnose(exportState,subname//':ES',rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - if (mastertask) then - call log_clock_advance(clock, 'LND', logunit, rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - endif - endif - - call shr_log_setLogUnit (shrlogunit) - - call ESMF_LogWrite(subname//' done', ESMF_LOGMSG_INFO, rc=rc) - - end subroutine ModelAdvance - - !=============================================================================== - - subroutine state_setexport(exportState, rc) - - ! input/output variables - type(ESMF_State) , intent(inout) :: exportState - integer, intent(out) :: rc - - ! local variables - integer :: n, nf, nind - real(r8), pointer :: lat(:) - real(r8), pointer :: lon(:) - integer :: spatialDim - integer :: numOwnedElements - real(R8), pointer :: ownedElemCoords(:) - !-------------------------------------------------- - - rc = ESMF_SUCCESS - - call ESMF_MeshGet(mesh, spatialDim=spatialDim, numOwnedElements=numOwnedElements, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - allocate(ownedElemCoords(spatialDim*numOwnedElements)) - call ESMF_MeshGet(mesh, ownedElemCoords=ownedElemCoords) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - - allocate(lon(numownedElements)) - allocate(lat(numownedElements)) - do n = 1,numownedElements - lon(n) = ownedElemCoords(2*n-1) - lat(n) = ownedElemCoords(2*n) - end do - - ! Start from index 2 in order to Skip the scalar field here - do nf = 2,fldsFrLnd_num - if (fldsFrLnd(nf)%ungridded_ubound == 0) then - call field_setexport(exportState, trim(fldsFrLnd(nf)%stdname), lon, lat, nf=nf, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - else - do nind = 1,fldsFrLnd(nf)%ungridded_ubound - call field_setexport(exportState, trim(fldsFrLnd(nf)%stdname), lon, lat, nf=nf+nind-1, & - ungridded_index=nind, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - end do - end if - end do - - deallocate(lon) - deallocate(lat) - - end subroutine state_setexport - - !=============================================================================== - - subroutine field_setexport(exportState, fldname, lon, lat, nf, ungridded_index, rc) - - use shr_const_mod , only : pi=>shr_const_pi - - ! intput/otuput variables - type(ESMF_State) , intent(inout) :: exportState - character(len=*) , intent(in) :: fldname - real(r8) , intent(in) :: lon(:) - real(r8) , intent(in) :: lat(:) - integer , intent(in) :: nf - integer, optional , intent(in) :: ungridded_index - integer , intent(out) :: rc - - ! local variables - integer :: i, ncomp - type(ESMF_Field) :: lfield - real(r8), pointer :: data1d(:) - real(r8), pointer :: data2d(:,:) - !-------------------------------------------------- - - rc = ESMF_SUCCESS - - call ESMF_StateGet(exportState, itemName=trim(fldname), field=lfield, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ncomp = 2 - if (present(ungridded_index)) then - call ESMF_FieldGet(lfield, farrayPtr=data2d, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - if (gridToFieldMap == 1) then - do i = 1,size(data2d, dim=1) - data2d(i,ungridded_index) = (nf*100) * cos(pi*lat(i)/180.0_R8) * & - sin((pi*lon(i)/180.0_R8) - (ncomp-1)*(pi/3.0_R8) ) + (ncomp*10.0_R8) - end do - else if (gridToFieldMap == 2) then - do i = 1,size(data2d, dim=2) - data2d(ungridded_index,i) = (nf*100) * cos(pi*lat(i)/180.0_R8) * & - sin((pi*lon(i)/180.0_R8) - (ncomp-1)*(pi/3.0_R8) ) + (ncomp*10.0_R8) - end do - end if - else - call ESMF_FieldGet(lfield, farrayPtr=data1d, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - if (fldname == 'Sl_lfrin') then - data1d(:) = 1._r8 - else - do i = 1,size(data1d) - data1d(i) = (nf*100) * cos(pi*lat(i)/180.0_R8) * & - sin((pi*lon(i)/180.0_R8) - (ncomp-1)*(pi/3.0_R8) ) + (ncomp*10.0_R8) - end do - end if - end if - - end subroutine field_setexport - - !=============================================================================== - subroutine ModelFinalize(gcomp, rc) - type(ESMF_GridComp) :: gcomp - integer, intent(out) :: rc - !------------------------------------------------------------------------------- - - rc = ESMF_SUCCESS - - if (mastertask) then - write(logunit,*) - write(logunit,*) 'xlnd: end of main integration loop' - write(logunit,*) - end if - end subroutine ModelFinalize - -end module lnd_comp_nuopc diff --git a/CIME/non_py/src/components/xcpl_comps_nuopc/xocn/cime_config/buildlib b/CIME/non_py/src/components/xcpl_comps_nuopc/xocn/cime_config/buildlib deleted file mode 120000 index 7766f77f5bc..00000000000 --- a/CIME/non_py/src/components/xcpl_comps_nuopc/xocn/cime_config/buildlib +++ /dev/null @@ -1 +0,0 @@ -../../../../../../build_scripts/buildlib.internal_components \ No newline at end of file diff --git a/CIME/non_py/src/components/xcpl_comps_nuopc/xocn/cime_config/buildlib_cmake b/CIME/non_py/src/components/xcpl_comps_nuopc/xocn/cime_config/buildlib_cmake deleted file mode 120000 index 7766f77f5bc..00000000000 --- a/CIME/non_py/src/components/xcpl_comps_nuopc/xocn/cime_config/buildlib_cmake +++ /dev/null @@ -1 +0,0 @@ -../../../../../../build_scripts/buildlib.internal_components \ No newline at end of file diff --git a/CIME/non_py/src/components/xcpl_comps_nuopc/xocn/cime_config/buildnml b/CIME/non_py/src/components/xcpl_comps_nuopc/xocn/cime_config/buildnml deleted file mode 100755 index 7158056b462..00000000000 --- a/CIME/non_py/src/components/xcpl_comps_nuopc/xocn/cime_config/buildnml +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env python3 - -""" -build data model library -""" - -import sys, os - -_CIMEROOT = os.path.join( - os.path.dirname(os.path.abspath(__file__)), "..", "..", "..", "..", ".." -) -sys.path.append(os.path.join(_CIMEROOT, "CIME", "Tools")) - -from standard_script_setup import * -from CIME.buildnml import build_xcpl_nml, parse_input -from CIME.case import Case - - -def buildnml(case, caseroot, compname): - if compname != "xocn": - raise AttributeError - build_xcpl_nml(case, caseroot, compname) - - -def _main_func(): - caseroot = parse_input(sys.argv) - with Case(caseroot) as case: - buildnml(case, caseroot, "xocn") - - -if __name__ == "__main__": - _main_func() diff --git a/CIME/non_py/src/components/xcpl_comps_nuopc/xocn/cime_config/config_component.xml b/CIME/non_py/src/components/xcpl_comps_nuopc/xocn/cime_config/config_component.xml deleted file mode 100644 index f68d1ff4701..00000000000 --- a/CIME/non_py/src/components/xcpl_comps_nuopc/xocn/cime_config/config_component.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - Dead ocean component - - - - char - xocn - xocn - case_comp - env_case.xml - Name of ocean component - - - - - - ========================================= - XOCN naming conventions in compset name - ========================================= - - - diff --git a/CIME/non_py/src/components/xcpl_comps_nuopc/xocn/src/ocn_comp_nuopc.F90 b/CIME/non_py/src/components/xcpl_comps_nuopc/xocn/src/ocn_comp_nuopc.F90 deleted file mode 100644 index 87f8ca25102..00000000000 --- a/CIME/non_py/src/components/xcpl_comps_nuopc/xocn/src/ocn_comp_nuopc.F90 +++ /dev/null @@ -1,475 +0,0 @@ -module ocn_comp_nuopc - - !---------------------------------------------------------------------------- - ! This is the NUOPC cap for XOCN - !---------------------------------------------------------------------------- - - use ESMF - use NUOPC , only : NUOPC_CompDerive, NUOPC_CompSetEntryPoint, NUOPC_CompSpecialize - use NUOPC , only : NUOPC_CompAttributeGet, NUOPC_Advertise - use NUOPC_Model , only : model_routine_SS => SetServices - use NUOPC_Model , only : model_label_Advance => label_Advance - use NUOPC_Model , only : model_label_SetRunClock => label_SetRunClock - use NUOPC_Model , only : model_label_Finalize => label_Finalize - use NUOPC_Model , only : NUOPC_ModelGet, SetVM - use shr_sys_mod , only : shr_sys_abort - use shr_kind_mod , only : r8=>shr_kind_r8, i8=>shr_kind_i8, cl=>shr_kind_cl, cs=>shr_kind_cs - use shr_log_mod , only : shr_log_getlogunit, shr_log_setlogunit - use dead_methods_mod , only : chkerr, state_setscalar, state_diagnose, alarmInit, memcheck - use dead_methods_mod , only : set_component_logging, get_component_instance, log_clock_advance - use dead_nuopc_mod , only : dead_read_inparms, ModelInitPhase, ModelSetRunClock - use dead_nuopc_mod , only : fld_list_add, fld_list_realize, fldsMax, fld_list_type - - implicit none - private ! except - - public :: SetServices - public :: SetVM - !-------------------------------------------------------------------------- - ! Private module data - !-------------------------------------------------------------------------- - - character(len=CL) :: flds_scalar_name = '' - integer :: flds_scalar_num = 0 - integer :: flds_scalar_index_nx = 0 - integer :: flds_scalar_index_ny = 0 - integer :: flds_scalar_index_nextsw_cday = 0._r8 - - integer :: fldsToOcn_num = 0 - integer :: fldsFrOcn_num = 0 - type (fld_list_type) :: fldsToOcn(fldsMax) - type (fld_list_type) :: fldsFrOcn(fldsMax) - integer, parameter :: gridTofieldMap = 2 ! ungridded dimension is innermost - - type(ESMF_Mesh) :: mesh - integer :: nxg ! global dim i-direction - integer :: nyg ! global dim j-direction - integer :: my_task ! my task in mpi communicator mpicom - integer :: inst_index ! number of current instance (ie. 1) - character(len=16) :: inst_name ! fullname of current instance (ie. "ocn_0001") - character(len=16) :: inst_suffix = "" ! char string associated with instance (ie. "_0001" or "") - integer :: logunit ! logging unit number - integer ,parameter :: master_task=0 ! task number of master task - logical :: mastertask - integer :: dbug = 0 - character(*),parameter :: modName = "(xocn_comp_nuopc)" - character(*),parameter :: u_FILE_u = & - __FILE__ - -!=============================================================================== -contains -!=============================================================================== - - subroutine SetServices(gcomp, rc) - - type(ESMF_GridComp) :: gcomp - integer, intent(out) :: rc - character(len=*),parameter :: subname=trim(modName)//':(SetServices) ' - - rc = ESMF_SUCCESS - call ESMF_LogWrite(subname//' called', ESMF_LOGMSG_INFO, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! the NUOPC gcomp component will register the generic methods - call NUOPC_CompDerive(gcomp, model_routine_SS, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! switching to IPD versions - call ESMF_GridCompSetEntryPoint(gcomp, ESMF_METHOD_INITIALIZE, & - userRoutine=ModelInitPhase, phase=0, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! set entry point for methods that require specific implementation - call NUOPC_CompSetEntryPoint(gcomp, ESMF_METHOD_INITIALIZE, phaseLabelList=(/"IPDv01p1"/), & - userRoutine=InitializeAdvertise, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call NUOPC_CompSetEntryPoint(gcomp, ESMF_METHOD_INITIALIZE, phaseLabelList=(/"IPDv01p3"/), & - userRoutine=InitializeRealize, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! attach specializing method(s) - call NUOPC_CompSpecialize(gcomp, specLabel=model_label_Advance, specRoutine=ModelAdvance, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call ESMF_MethodRemove(gcomp, label=model_label_SetRunClock, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - call NUOPC_CompSpecialize(gcomp, specLabel=model_label_SetRunClock, specRoutine=ModelSetRunClock, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call NUOPC_CompSpecialize(gcomp, specLabel=model_label_Finalize, specRoutine=ModelFinalize, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call ESMF_LogWrite(subname//' done', ESMF_LOGMSG_INFO, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - end subroutine SetServices - - !=============================================================================== - - subroutine InitializeAdvertise(gcomp, importState, exportState, clock, rc) - - ! input/output variables - type(ESMF_GridComp) :: gcomp - type(ESMF_State) :: importState, exportState - type(ESMF_Clock) :: clock - integer, intent(out) :: rc - - ! local variables - type(ESMF_VM) :: vm - integer :: n - integer :: lsize ! local array size - integer :: shrlogunit ! original log unit - character(CL) :: cvalue - character(len=CL) :: logmsg - logical :: isPresent, isSet - character(len=*),parameter :: subname=trim(modName)//':(InitializeAdvertise) ' - !------------------------------------------------------------------------------- - - rc = ESMF_SUCCESS - call ESMF_LogWrite(subname//' called', ESMF_LOGMSG_INFO, rc=rc) - - call ESMF_GridCompGet(gcomp, vm=vm, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - call ESMF_VMGet(vm, localpet=my_task, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - mastertask = (my_task == master_task) - - ! determine instance information - call get_component_instance(gcomp, inst_suffix, inst_index, rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! set logunit and set shr logging to my log file - call set_component_logging(gcomp, my_task==master_task, logunit, shrlogunit, rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! Initialize xocn - call dead_read_inparms('ocn', inst_suffix, logunit, nxg, nyg) - - ! advertise import and export fields - call NUOPC_CompAttributeGet(gcomp, name="ScalarFieldName", value=cvalue, isPresent=isPresent, isSet=isSet, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - if (isPresent .and. isSet) then - flds_scalar_name = trim(cvalue) - call ESMF_LogWrite(trim(subname)//' flds_scalar_name = '//trim(flds_scalar_name), ESMF_LOGMSG_INFO) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - else - call shr_sys_abort(subname//'Need to set attribute ScalarFieldName') - endif - - call NUOPC_CompAttributeGet(gcomp, name="ScalarFieldCount", value=cvalue, isPresent=isPresent, isSet=isSet, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - if (isPresent .and. isSet) then - read(cvalue, *) flds_scalar_num - write(logmsg,*) flds_scalar_num - call ESMF_LogWrite(trim(subname)//' flds_scalar_num = '//trim(logmsg), ESMF_LOGMSG_INFO) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - else - call shr_sys_abort(subname//'Need to set attribute ScalarFieldCount') - endif - - call NUOPC_CompAttributeGet(gcomp, name="ScalarFieldIdxGridNX", value=cvalue, isPresent=isPresent, isSet=isSet, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - if (isPresent .and. isSet) then - read(cvalue,*) flds_scalar_index_nx - write(logmsg,*) flds_scalar_index_nx - call ESMF_LogWrite(trim(subname)//' : flds_scalar_index_nx = '//trim(logmsg), ESMF_LOGMSG_INFO) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - else - call shr_sys_abort(subname//'Need to set attribute ScalarFieldIdxGridNX') - endif - - call NUOPC_CompAttributeGet(gcomp, name="ScalarFieldIdxGridNY", value=cvalue, isPresent=isPresent, isSet=isSet, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - if (isPresent .and. isSet) then - read(cvalue,*) flds_scalar_index_ny - write(logmsg,*) flds_scalar_index_ny - call ESMF_LogWrite(trim(subname)//' : flds_scalar_index_ny = '//trim(logmsg), ESMF_LOGMSG_INFO) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - else - call shr_sys_abort(subname//'Need to set attribute ScalarFieldIdxGridNY') - endif - - if (nxg /= 0 .and. nyg /= 0) then - - call fld_list_add(fldsFrOcn_num, fldsFrOcn, trim(flds_scalar_name)) - call fld_list_add(fldsFrOcn_num, fldsFrOcn, "So_omask" ) - call fld_list_add(fldsFrOcn_num, fldsFrOcn, "So_t" ) - call fld_list_add(fldsFrOcn_num, fldsFrOcn, "So_s" ) - call fld_list_add(fldsFrOcn_num, fldsFrOcn, "So_u" ) - call fld_list_add(fldsFrOcn_num, fldsFrOcn, "So_v" ) - call fld_list_add(fldsFrOcn_num, fldsFrOcn, "So_dhdx" ) - call fld_list_add(fldsFrOcn_num, fldsFrOcn, "So_dhdy" ) - call fld_list_add(fldsFrOcn_num, fldsFrOcn, "So_bldepth" ) - call fld_list_add(fldsFrOcn_num, fldsFrOcn, "Fioo_q" ) - - call fld_list_add(fldsToOcn_num, fldsToOcn, trim(flds_scalar_name)) - call fld_list_add(fldsToOcn_num, fldsToOcn, "Faxa_rain" ) - call fld_list_add(fldsToOcn_num, fldsToOcn, "Faxa_snow" ) - call fld_list_add(fldsToOcn_num, fldsToOcn, "Faxa_lwdn" ) - call fld_list_add(fldsToOcn_num, fldsToOcn, "Faxa_swndr" ) - call fld_list_add(fldsToOcn_num, fldsToOcn, "Faxa_swvdr" ) - call fld_list_add(fldsToOcn_num, fldsToOcn, "Faxa_swndf" ) - call fld_list_add(fldsToOcn_num, fldsToOcn, "Faxa_swvdf" ) - call fld_list_add(fldsToOcn_num, fldsToOcn, "Foxx_taux" ) - call fld_list_add(fldsToOcn_num, fldsToOcn, "Foxx_tauy" ) - call fld_list_add(fldsToOcn_num, fldsToOcn, "Foxx_sen" ) - call fld_list_add(fldsToOcn_num, fldsToOcn, "Foxx_lat" ) - call fld_list_add(fldsToOcn_num, fldsToOcn, "Foxx_lwup" ) - call fld_list_add(fldsToOcn_num, fldsToOcn, "Foxx_evap" ) - call fld_list_add(fldsToOcn_num, fldsToOcn, "Fioi_salt" ) - call fld_list_add(fldsToOcn_num, fldsToOcn, "Foxx_rofl" ) - call fld_list_add(fldsToOcn_num, fldsToOcn, "Foxx_rofi" ) - call fld_list_add(fldsToOcn_num, fldsToOcn, "Sa_pslv" ) - - do n = 1,fldsFrOcn_num - if(mastertask) write(logunit,*)'Advertising From Xocn ',trim(fldsFrOcn(n)%stdname) - call NUOPC_Advertise(exportState, standardName=fldsFrOcn(n)%stdname, & - TransferOfferGeomObject='will provide', rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - enddo - - do n = 1,fldsToOcn_num - if(mastertask) write(logunit,*)'Advertising To Xocn',trim(fldsToOcn(n)%stdname) - call NUOPC_Advertise(importState, standardName=fldsToOcn(n)%stdname, & - TransferOfferGeomObject='will provide', rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - enddo - end if - - ! Reset shr logging to original values - call shr_log_setLogUnit (shrlogunit) - - end subroutine InitializeAdvertise - - !=============================================================================== - subroutine InitializeRealize(gcomp, importState, exportState, clock, rc) - - ! input/output variables - type(ESMF_GridComp) :: gcomp - type(ESMF_State) :: importState, exportState - type(ESMF_Clock) :: clock - integer, intent(out) :: rc - - ! local variables - integer :: shrlogunit ! original log unit - character(ESMF_MAXSTR) :: cvalue ! config data - character(len=*),parameter :: subname=trim(modName)//':(InitializeRealize: xocn) ' - !------------------------------------------------------------------------------- - - rc = ESMF_SUCCESS - - ! Reset shr logging to my log file - call shr_log_getLogUnit (shrlogunit) - call shr_log_setLogUnit (logunit) - - ! generate the mesh - call NUOPC_CompAttributeGet(gcomp, name='mesh_ocn', value=cvalue, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - mesh = ESMF_MeshCreate(filename=trim(cvalue), fileformat=ESMF_FILEFORMAT_ESMFMESH, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - - ! realize the actively coupled fields, now that a mesh is established - ! NUOPC_Realize "realizes" a previously advertised field in the importState and exportState - ! by replacing the advertised fields with the newly created fields of the same name. - call fld_list_realize( & - state=ExportState, & - fldlist=fldsFrOcn, & - numflds=fldsFrOcn_num, & - flds_scalar_name=flds_scalar_name, & - flds_scalar_num=flds_scalar_num, & - tag=subname//':docnExport',& - mesh=mesh, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call fld_list_realize( & - state=importState, & - fldList=fldsToOcn, & - numflds=fldsToOcn_num, & - flds_scalar_name=flds_scalar_name, & - flds_scalar_num=flds_scalar_num, & - tag=subname//':docnImport',& - mesh=mesh, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! Pack export state - call state_setexport(exportState, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - call State_SetScalar(dble(nxg),flds_scalar_index_nx, exportState, flds_scalar_name, flds_scalar_num, rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - call State_SetScalar(dble(nyg),flds_scalar_index_ny, exportState, flds_scalar_name, flds_scalar_num, rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! diagnostics - if (dbug > 1) then - call state_diagnose(exportState,subname//':ES',rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - endif - - call shr_log_setLogUnit (shrlogunit) - - end subroutine InitializeRealize - - !=============================================================================== - subroutine ModelAdvance(gcomp, rc) - - ! intput/output variables - type(ESMF_GridComp) :: gcomp - integer, intent(out) :: rc - - ! local variables - type(ESMF_Clock) :: clock - type(ESMF_State) :: exportState - integer :: shrlogunit ! original log unit - character(len=*),parameter :: subname=trim(modName)//':(ModelAdvance) ' - !------------------------------------------------------------------------------- - - rc = ESMF_SUCCESS - - call memcheck(subname, 3, mastertask) - - call shr_log_getLogUnit (shrlogunit) - call shr_log_setLogUnit (logunit) - - ! Pack export state - call NUOPC_ModelGet(gcomp, modelClock=clock, exportState=exportState, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - call state_setexport(exportState, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! diagnostics - if (dbug > 1) then - call state_diagnose(exportState,subname//':ES',rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - endif - - call shr_log_setLogUnit (shrlogunit) - - end subroutine ModelAdvance - - !=============================================================================== - subroutine state_setexport(exportState, rc) - - ! input/output variables - type(ESMF_State) , intent(inout) :: exportState - integer, intent(out) :: rc - - ! local variables - integer :: n, nf, nind - real(r8), pointer :: lat(:) - real(r8), pointer :: lon(:) - integer :: spatialDim - integer :: numOwnedElements - real(R8), pointer :: ownedElemCoords(:) - !-------------------------------------------------- - - rc = ESMF_SUCCESS - - call ESMF_MeshGet(mesh, spatialDim=spatialDim, numOwnedElements=numOwnedElements, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - allocate(ownedElemCoords(spatialDim*numOwnedElements)) - call ESMF_MeshGet(mesh, ownedElemCoords=ownedElemCoords) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - - allocate(lon(numownedElements)) - allocate(lat(numownedElements)) - do n = 1,numownedElements - lon(n) = ownedElemCoords(2*n-1) - lat(n) = ownedElemCoords(2*n) - end do - - ! Start from index 2 in order to Skip the scalar field here - do nf = 2,fldsFrOcn_num - if (fldsFrOcn(nf)%ungridded_ubound == 0) then - call field_setexport(exportState, trim(fldsFrOcn(nf)%stdname), lon, lat, nf=nf, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - else - do nind = 1,fldsFrOcn(nf)%ungridded_ubound - call field_setexport(exportState, trim(fldsFrOcn(nf)%stdname), lon, lat, nf=nf, & - ungridded_index=nind, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - end do - end if - end do - - deallocate(lon) - deallocate(lat) - - end subroutine state_setexport - - !=============================================================================== - - subroutine field_setexport(exportState, fldname, lon, lat, nf, ungridded_index, rc) - - use shr_const_mod , only : pi=>shr_const_pi - - ! intput/otuput variables - type(ESMF_State) , intent(inout) :: exportState - character(len=*) , intent(in) :: fldname - real(r8) , intent(in) :: lon(:) - real(r8) , intent(in) :: lat(:) - integer , intent(in) :: nf - integer, optional , intent(in) :: ungridded_index - integer , intent(out) :: rc - - ! local variables - integer :: i, ncomp - type(ESMF_Field) :: lfield - real(r8), pointer :: data1d(:) - real(r8), pointer :: data2d(:,:) - !-------------------------------------------------- - - rc = ESMF_SUCCESS - - call ESMF_StateGet(exportState, itemName=trim(fldname), field=lfield, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ncomp = 4 - if (present(ungridded_index)) then - call ESMF_FieldGet(lfield, farrayPtr=data2d, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - if (gridToFieldMap == 1) then - do i = 1,size(data2d, dim=1) - data2d(i,ungridded_index) = (nf*100) * cos(pi*lat(i)/180.0_R8) * & - sin((pi*lon(i)/180.0_R8) - (ncomp-1)*(pi/3.0_R8) ) + (ncomp*10.0_R8) - end do - else if (gridToFieldMap == 2) then - do i = 1,size(data2d, dim=2) - data2d(ungridded_index,i) = (nf*100) * cos(pi*lat(i)/180.0_R8) * & - sin((pi*lon(i)/180.0_R8) - (ncomp-1)*(pi/3.0_R8) ) + (ncomp*10.0_R8) - end do - end if - else - call ESMF_FieldGet(lfield, farrayPtr=data1d, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - do i = 1,size(data1d) - data1d(i) = (nf*100) * cos(pi*lat(i)/180.0_R8) * & - sin((pi*lon(i)/180.0_R8) - (ncomp-1)*(pi/3.0_R8) ) + (ncomp*10.0_R8) - end do - end if - - if (fldname == 'So_omask') then - do i = 1,size(data1d) - !data1d(i) = float(nint(min(1.0_R8,max(0.0_R8,data1d(i))))) - data1d(i) = 0._r8 - end do - end if - - end subroutine field_setexport - - !=============================================================================== - subroutine ModelFinalize(gcomp, rc) - type(ESMF_GridComp) :: gcomp - integer, intent(out) :: rc - !------------------------------------------------------------------------------- - - rc = ESMF_SUCCESS - - if (mastertask) then - write(logunit,*) - write(logunit,*) 'xocn: end of main integration loop' - write(logunit,*) - end if - end subroutine ModelFinalize - -end module ocn_comp_nuopc diff --git a/CIME/non_py/src/components/xcpl_comps_nuopc/xrof/cime_config/buildlib b/CIME/non_py/src/components/xcpl_comps_nuopc/xrof/cime_config/buildlib deleted file mode 120000 index 7766f77f5bc..00000000000 --- a/CIME/non_py/src/components/xcpl_comps_nuopc/xrof/cime_config/buildlib +++ /dev/null @@ -1 +0,0 @@ -../../../../../../build_scripts/buildlib.internal_components \ No newline at end of file diff --git a/CIME/non_py/src/components/xcpl_comps_nuopc/xrof/cime_config/buildlib_cmake b/CIME/non_py/src/components/xcpl_comps_nuopc/xrof/cime_config/buildlib_cmake deleted file mode 120000 index 7766f77f5bc..00000000000 --- a/CIME/non_py/src/components/xcpl_comps_nuopc/xrof/cime_config/buildlib_cmake +++ /dev/null @@ -1 +0,0 @@ -../../../../../../build_scripts/buildlib.internal_components \ No newline at end of file diff --git a/CIME/non_py/src/components/xcpl_comps_nuopc/xrof/cime_config/buildnml b/CIME/non_py/src/components/xcpl_comps_nuopc/xrof/cime_config/buildnml deleted file mode 100755 index bf23e8913e5..00000000000 --- a/CIME/non_py/src/components/xcpl_comps_nuopc/xrof/cime_config/buildnml +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env python3 - -""" -build data model library -""" - -import sys, os - -_CIMEROOT = os.path.join( - os.path.dirname(os.path.abspath(__file__)), "..", "..", "..", "..", ".." -) -sys.path.append(os.path.join(_CIMEROOT, "CIME", "Tools")) - -from standard_script_setup import * -from CIME.buildnml import build_xcpl_nml, parse_input -from CIME.case import Case - - -def buildnml(case, caseroot, compname): - if compname != "xrof": - raise AttributeError - build_xcpl_nml(case, caseroot, compname) - - -def _main_func(): - caseroot = parse_input(sys.argv) - with Case(caseroot) as case: - buildnml(case, caseroot, "xrof") - - -if __name__ == "__main__": - _main_func() diff --git a/CIME/non_py/src/components/xcpl_comps_nuopc/xrof/cime_config/config_component.xml b/CIME/non_py/src/components/xcpl_comps_nuopc/xrof/cime_config/config_component.xml deleted file mode 100644 index e1663cbbb7b..00000000000 --- a/CIME/non_py/src/components/xcpl_comps_nuopc/xrof/cime_config/config_component.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - Dead river component - - - - char - xrof - xrof - case_comp - env_case.xml - Name of river component - - - - char - ACTIVE,NULL - NULL - - ACTIVE - ACTIVE - - build_component_xrof - env_build.xml - mode for xrof flood feature, NULL means xrof flood is turned off - - - - - ========================================= - XROF naming conventions in compset name - ========================================= - - - diff --git a/CIME/non_py/src/components/xcpl_comps_nuopc/xrof/src/rof_comp_nuopc.F90 b/CIME/non_py/src/components/xcpl_comps_nuopc/xrof/src/rof_comp_nuopc.F90 deleted file mode 100644 index 1b5b9dd4901..00000000000 --- a/CIME/non_py/src/components/xcpl_comps_nuopc/xrof/src/rof_comp_nuopc.F90 +++ /dev/null @@ -1,473 +0,0 @@ -module rof_comp_nuopc - - !---------------------------------------------------------------------------- - ! This is the NUOPC cap for XROF - !---------------------------------------------------------------------------- - - use ESMF - use NUOPC , only : NUOPC_CompDerive, NUOPC_CompSetEntryPoint, NUOPC_CompSpecialize - use NUOPC , only : NUOPC_CompAttributeGet, NUOPC_Advertise - use NUOPC_Model , only : model_routine_SS => SetServices - use NUOPC_Model , only : model_label_Advance => label_Advance - use NUOPC_Model , only : model_label_SetRunClock => label_SetRunClock - use NUOPC_Model , only : model_label_Finalize => label_Finalize - use NUOPC_Model , only : NUOPC_ModelGet, SetVM - use shr_sys_mod , only : shr_sys_abort - use shr_kind_mod , only : r8=>shr_kind_r8, i8=>shr_kind_i8, cl=>shr_kind_cl, cs=>shr_kind_cs - use shr_log_mod , only : shr_log_getlogunit, shr_log_setlogunit - use dead_methods_mod , only : chkerr, state_setscalar, state_diagnose, alarmInit, memcheck - use dead_methods_mod , only : set_component_logging, get_component_instance, log_clock_advance - use dead_nuopc_mod , only : dead_read_inparms, ModelInitPhase, ModelSetRunClock - use dead_nuopc_mod , only : fld_list_add, fld_list_realize, fldsMax, fld_list_type - - implicit none - private ! except - - public :: SetServices - public :: SetVM - !-------------------------------------------------------------------------- - ! Private module data - !-------------------------------------------------------------------------- - - character(len=CL) :: flds_scalar_name = '' - integer :: flds_scalar_num = 0 - integer :: flds_scalar_index_nx = 0 - integer :: flds_scalar_index_ny = 0 - integer :: flds_scalar_index_nextsw_cday = 0 - - integer :: fldsToRof_num = 0 - integer :: fldsFrRof_num = 0 - type (fld_list_type) :: fldsToRof(fldsMax) - type (fld_list_type) :: fldsFrRof(fldsMax) - integer, parameter :: gridTofieldMap = 2 ! ungridded dimension is innermost - - type(ESMF_Mesh) :: mesh - integer :: nxg ! global dim i-direction - integer :: nyg ! global dim j-direction - integer :: my_task ! my task in mpi - integer :: inst_index ! number of current instance (ie. 1) - character(len=16) :: inst_suffix = "" ! char string associated with instance (ie. "_0001" or "") - integer :: logunit ! logging unit number - integer ,parameter :: master_task=0 ! task number of master task - logical :: mastertask - integer :: dbug = 0 - character(*),parameter :: modName = "(xrof_comp_nuopc)" - character(*),parameter :: u_FILE_u = & - __FILE__ - -!=============================================================================== -contains -!=============================================================================== - - subroutine SetServices(gcomp, rc) - - type(ESMF_GridComp) :: gcomp - integer, intent(out) :: rc - character(len=*),parameter :: subname=trim(modName)//':(SetServices) ' - - rc = ESMF_SUCCESS - call ESMF_LogWrite(subname//' called', ESMF_LOGMSG_INFO, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! the NUOPC gcomp component will register the generic methods - call NUOPC_CompDerive(gcomp, model_routine_SS, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! switching to IPD versions - call ESMF_GridCompSetEntryPoint(gcomp, ESMF_METHOD_INITIALIZE, & - userRoutine=ModelInitPhase, phase=0, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! set entry point for methods that require specific implementation - call NUOPC_CompSetEntryPoint(gcomp, ESMF_METHOD_INITIALIZE, phaseLabelList=(/"IPDv01p1"/), & - userRoutine=InitializeAdvertise, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call NUOPC_CompSetEntryPoint(gcomp, ESMF_METHOD_INITIALIZE, phaseLabelList=(/"IPDv01p3"/), & - userRoutine=InitializeRealize, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! attach specializing method(s) - call NUOPC_CompSpecialize(gcomp, specLabel=model_label_Advance, specRoutine=ModelAdvance, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call ESMF_MethodRemove(gcomp, label=model_label_SetRunClock, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - call NUOPC_CompSpecialize(gcomp, specLabel=model_label_SetRunClock, specRoutine=ModelSetRunClock, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call NUOPC_CompSpecialize(gcomp, specLabel=model_label_Finalize, specRoutine=ModelFinalize, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call ESMF_LogWrite(subname//' done', ESMF_LOGMSG_INFO, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - end subroutine SetServices - - !=============================================================================== - subroutine InitializeAdvertise(gcomp, importState, exportState, clock, rc) - - ! input/output variables - type(ESMF_GridComp) :: gcomp - type(ESMF_State) :: importState, exportState - type(ESMF_Clock) :: clock - integer, intent(out) :: rc - - ! local variables - type(ESMF_VM) :: vm - character(CS) :: stdname - integer :: n - integer :: lsize ! local array size - integer :: shrlogunit ! original log unit - character(CL) :: cvalue - character(len=CL) :: logmsg - logical :: isPresent, isSet - character(len=*),parameter :: subname=trim(modName)//':(InitializeAdvertise) ' - !------------------------------------------------------------------------------- - - rc = ESMF_SUCCESS - - call ESMF_GridCompGet(gcomp, vm=vm, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - call ESMF_VMGet(vm, localpet=my_task, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - mastertask = (my_task == master_task) - - ! determine instance information - call get_component_instance(gcomp, inst_suffix, inst_index, rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - - ! set logunit and set shr logging to my log file - call set_component_logging(gcomp, mastertask, logunit, shrlogunit, rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - - ! Initialize xrof - call dead_read_inparms('rof', inst_suffix, logunit, nxg, nyg) - - !-------------------------------- - ! advertise import and export fields - !-------------------------------- - - call NUOPC_CompAttributeGet(gcomp, name="ScalarFieldName", value=cvalue, isPresent=isPresent, isSet=isSet, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - if (isPresent .and. isSet) then - flds_scalar_name = trim(cvalue) - call ESMF_LogWrite(trim(subname)//' flds_scalar_name = '//trim(flds_scalar_name), ESMF_LOGMSG_INFO) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - else - call shr_sys_abort(subname//'Need to set attribute ScalarFieldName') - endif - - call NUOPC_CompAttributeGet(gcomp, name="ScalarFieldCount", value=cvalue, isPresent=isPresent, isSet=isSet, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - if (isPresent .and. isSet) then - read(cvalue, *) flds_scalar_num - write(logmsg,*) flds_scalar_num - call ESMF_LogWrite(trim(subname)//' flds_scalar_num = '//trim(logmsg), ESMF_LOGMSG_INFO) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - else - call shr_sys_abort(subname//'Need to set attribute ScalarFieldCount') - endif - - call NUOPC_CompAttributeGet(gcomp, name="ScalarFieldIdxGridNX", value=cvalue, isPresent=isPresent, isSet=isSet, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - if (isPresent .and. isSet) then - read(cvalue,*) flds_scalar_index_nx - write(logmsg,*) flds_scalar_index_nx - call ESMF_LogWrite(trim(subname)//' : flds_scalar_index_nx = '//trim(logmsg), ESMF_LOGMSG_INFO) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - else - call shr_sys_abort(subname//'Need to set attribute ScalarFieldIdxGridNX') - endif - - call NUOPC_CompAttributeGet(gcomp, name="ScalarFieldIdxGridNY", value=cvalue, isPresent=isPresent, isSet=isSet, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - if (isPresent .and. isSet) then - read(cvalue,*) flds_scalar_index_ny - write(logmsg,*) flds_scalar_index_ny - call ESMF_LogWrite(trim(subname)//' : flds_scalar_index_ny = '//trim(logmsg), ESMF_LOGMSG_INFO) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - else - call shr_sys_abort(subname//'Need to set attribute ScalarFieldIdxGridNY') - endif - - if (nxg /= 0 .and. nyg /= 0) then - - call fld_list_add(fldsFrRof_num, fldsFrRof, trim(flds_scalar_name)) - call fld_list_add(fldsFrRof_num, fldsFrRof, 'Forr_rofl') - call fld_list_add(fldsFrRof_num, fldsFrRof, 'Forr_rofi') - call fld_list_add(fldsFrRof_num, fldsFrRof, 'Flrr_flood') - call fld_list_add(fldsFrRof_num, fldsFrRof, 'Flrr_volr') - call fld_list_add(fldsFrRof_num, fldsFrRof, 'Flrr_volrmch') - - call fld_list_add(fldsToRof_num, fldsToRof, trim(flds_scalar_name)) - call fld_list_add(fldsToRof_num, fldsToRof, 'Flrl_rofsur') - call fld_list_add(fldsToRof_num, fldsToRof, 'Flrl_rofgwl') - call fld_list_add(fldsToRof_num, fldsToRof, 'Flrl_rofsub') - call fld_list_add(fldsToRof_num, fldsToRof, 'Flrl_rofdto') - call fld_list_add(fldsToRof_num, fldsToRof, 'Flrl_rofi') - call fld_list_add(fldsToRof_num, fldsToRof, 'Flrl_irrig') - - do n = 1,fldsFrRof_num - if(mastertask) write(logunit,*)'Advertising From Xrof ',trim(fldsFrRof(n)%stdname) - call NUOPC_Advertise(exportState, standardName=fldsFrRof(n)%stdname, & - TransferOfferGeomObject='will provide', rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - enddo - - do n = 1,fldsToRof_num - if(mastertask) write(logunit,*)'Advertising To Xrof',trim(fldsToRof(n)%stdname) - call NUOPC_Advertise(importState, standardName=fldsToRof(n)%stdname, & - TransferOfferGeomObject='will provide', rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - enddo - end if - - if (dbug > 5) call ESMF_LogWrite(subname//' done', ESMF_LOGMSG_INFO, rc=rc) - - !---------------------------------------------------------------------------- - ! Reset shr logging to original values - !---------------------------------------------------------------------------- - - call shr_log_setLogUnit (shrlogunit) - - end subroutine InitializeAdvertise - - !=============================================================================== - subroutine InitializeRealize(gcomp, importState, exportState, clock, rc) - - ! input/output arguments - type(ESMF_GridComp) :: gcomp - type(ESMF_State) :: importState, exportState - type(ESMF_Clock) :: clock - integer, intent(out) :: rc - - ! local variables - integer :: shrlogunit ! original log unit - character(ESMF_MAXSTR) :: cvalue ! config data - character(len=*),parameter :: subname=trim(modName)//':(InitializeRealize) ' - !------------------------------------------------------------------------------- - - rc = ESMF_SUCCESS - - ! Reset shr logging to my log file - call shr_log_getLogUnit (shrlogunit) - call shr_log_setLogUnit (logUnit) - - - ! generate the mesh - call NUOPC_CompAttributeGet(gcomp, name='mesh_rof', value=cvalue, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - mesh = ESMF_MeshCreate(filename=trim(cvalue), fileformat=ESMF_FILEFORMAT_ESMFMESH, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - - ! realize the actively coupled fields, now that a mesh is established - ! NUOPC_Realize "realizes" a previously advertised field in the importState and exportState - ! by replacing the advertised fields with the newly created fields of the same name. - call fld_list_realize( & - state=ExportState, & - fldlist=fldsFrRof, & - numflds=fldsFrRof_num, & - flds_scalar_name=flds_scalar_name, & - flds_scalar_num=flds_scalar_num, & - tag=subname//':drofExport',& - mesh=mesh, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call fld_list_realize( & - state=importState, & - fldList=fldsToRof, & - numflds=fldsToRof_num, & - flds_scalar_name=flds_scalar_name, & - flds_scalar_num=flds_scalar_num, & - tag=subname//':drofImport',& - mesh=mesh, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - !-------------------------------- - ! Pack export state - !-------------------------------- - - call state_setexport(exportState, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - call State_SetScalar(dble(nxg),flds_scalar_index_nx, exportState, flds_scalar_name, flds_scalar_num, rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - call State_SetScalar(dble(nyg),flds_scalar_index_ny, exportState, flds_scalar_name, flds_scalar_num, rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - !-------------------------------- - ! diagnostics - !-------------------------------- - - if (dbug > 1) then - call State_diagnose(exportState,subname//':ES',rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - endif - - call shr_log_setLogUnit (shrlogunit) - - end subroutine InitializeRealize - - !=============================================================================== - subroutine ModelAdvance(gcomp, rc) - - ! input/output variables - type(ESMF_GridComp) :: gcomp - integer, intent(out) :: rc - - ! local variables - type(ESMF_Clock) :: clock - type(ESMF_State) :: exportState - integer :: shrlogunit ! original log unit - character(len=*),parameter :: subname=trim(modName)//':(ModelAdvance) ' - !------------------------------------------------------------------------------- - - rc = ESMF_SUCCESS - - if (dbug > 5) then - call ESMF_LogWrite(subname//' called', ESMF_LOGMSG_INFO, rc=rc) - end if - call memcheck(subname, 3, mastertask) - - call shr_log_getLogUnit (shrlogunit) - call shr_log_setLogUnit (logunit) - - ! Pack export state - call NUOPC_ModelGet(gcomp, modelClock=clock, exportState=exportState, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - call State_SetExport(exportState, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! diagnostics - if (dbug > 1) then - call State_diagnose(exportState,subname//':ES',rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - if (mastertask) then - call log_clock_advance(clock, 'XROF', logunit, rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - endif - endif - - call shr_log_setLogUnit (shrlogunit) - - if (dbug > 5) then - call ESMF_LogWrite(subname//' done', ESMF_LOGMSG_INFO, rc=rc) - end if - - end subroutine ModelAdvance - - !=============================================================================== - subroutine state_setexport(exportState, rc) - - ! input/output variables - type(ESMF_State) , intent(inout) :: exportState - integer, intent(out) :: rc - - ! local variables - integer :: n, nf, nind - real(r8), pointer :: lat(:) - real(r8), pointer :: lon(:) - integer :: spatialDim - integer :: numOwnedElements - real(R8), pointer :: ownedElemCoords(:) - !-------------------------------------------------- - - rc = ESMF_SUCCESS - - call ESMF_MeshGet(mesh, spatialDim=spatialDim, numOwnedElements=numOwnedElements, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - allocate(ownedElemCoords(spatialDim*numOwnedElements)) - call ESMF_MeshGet(mesh, ownedElemCoords=ownedElemCoords) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - - allocate(lon(numownedElements)) - allocate(lat(numownedElements)) - do n = 1,numownedElements - lon(n) = ownedElemCoords(2*n-1) - lat(n) = ownedElemCoords(2*n) - end do - - ! Start from index 2 in order to skip the scalar field - do nf = 2,fldsFrRof_num - if (fldsFrRof(nf)%ungridded_ubound == 0) then - call field_setexport(exportState, trim(fldsFrRof(nf)%stdname), lon, lat, nf=nf, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - else - do nind = 1,fldsFrRof(nf)%ungridded_ubound - call field_setexport(exportState, trim(fldsFrRof(nf)%stdname), lon, lat, nf=nf+nind-1, & - ungridded_index=nind, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - end do - end if - end do - - deallocate(lon) - deallocate(lat) - - end subroutine state_setexport - - !=============================================================================== - subroutine field_setexport(exportState, fldname, lon, lat, nf, ungridded_index, rc) - - use shr_const_mod , only : pi=>shr_const_pi - - ! intput/otuput variables - type(ESMF_State) , intent(inout) :: exportState - character(len=*) , intent(in) :: fldname - real(r8) , intent(in) :: lon(:) - real(r8) , intent(in) :: lat(:) - integer , intent(in) :: nf - integer, optional , intent(in) :: ungridded_index - integer , intent(out) :: rc - - ! local variables - integer :: i, ncomp - type(ESMF_Field) :: lfield - real(r8), pointer :: data1d(:) - real(r8), pointer :: data2d(:,:) - !-------------------------------------------------- - - rc = ESMF_SUCCESS - - call ESMF_StateGet(exportState, itemName=trim(fldname), field=lfield, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ncomp = 6 - if (present(ungridded_index)) then - call ESMF_FieldGet(lfield, farrayPtr=data2d, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - if (gridToFieldMap == 1) then - do i = 1,size(data2d, dim=1) - data2d(i,ungridded_index) = (nf+1) * 1.0_r8 - end do - else if (gridToFieldMap == 2) then - do i = 1,size(data2d, dim=2) - data2d(ungridded_index,i) = (nf+1) * 1.0_r8 - end do - end if - else - call ESMF_FieldGet(lfield, farrayPtr=data1d, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - do i = 1,size(data1d) - data1d(i) = (nf+1) * 1.0_r8 - end do - end if - - end subroutine field_setexport - - !=============================================================================== - subroutine ModelFinalize(gcomp, rc) - type(ESMF_GridComp) :: gcomp - integer, intent(out) :: rc - !------------------------------------------------------------------------------- - - rc = ESMF_SUCCESS - - if (mastertask) then - write(logunit,*) - write(logunit,*) 'xrof: end of main integration loop' - write(logunit,*) - end if - end subroutine ModelFinalize - -end module rof_comp_nuopc diff --git a/CIME/non_py/src/components/xcpl_comps_nuopc/xshare/dead_methods_mod.F90 b/CIME/non_py/src/components/xcpl_comps_nuopc/xshare/dead_methods_mod.F90 deleted file mode 100644 index a9ad38e2419..00000000000 --- a/CIME/non_py/src/components/xcpl_comps_nuopc/xshare/dead_methods_mod.F90 +++ /dev/null @@ -1,853 +0,0 @@ -module dead_methods_mod - - use ESMF , only : operator(<), operator(/=), operator(+) - use ESMF , only : operator(-), operator(*) , operator(>=) - use ESMF , only : operator(<=), operator(>), operator(==) - use ESMF , only : ESMF_LOGERR_PASSTHRU, ESMF_LogFoundError, ESMF_LOGMSG_ERROR, ESMF_MAXSTR - use ESMF , only : ESMF_SUCCESS, ESMF_LogWrite, ESMF_LOGMSG_INFO, ESMF_FAILURE - use ESMF , only : ESMF_State, ESMF_StateGet - use ESMF , only : ESMF_Field, ESMF_FieldGet - use ESMF , only : ESMF_GridComp, ESMF_GridCompGet, ESMF_GridCompSet - use ESMF , only : ESMF_GeomType_Flag, ESMF_FieldStatus_Flag - use ESMF , only : ESMF_Mesh, ESMF_MeshGet - use ESMF , only : ESMF_GEOMTYPE_MESH, ESMF_GEOMTYPE_GRID, ESMF_FIELDSTATUS_COMPLETE - use ESMF , only : ESMF_Clock, ESMF_ClockCreate, ESMF_ClockGet, ESMF_ClockSet - use ESMF , only : ESMF_ClockPrint, ESMF_ClockAdvance - use ESMF , only : ESMF_Alarm, ESMF_AlarmCreate, ESMF_AlarmGet, ESMF_AlarmSet - use ESMF , only : ESMF_Calendar, ESMF_CALKIND_NOLEAP, ESMF_CALKIND_GREGORIAN - use ESMF , only : ESMF_Time, ESMF_TimeGet, ESMF_TimeSet - use ESMF , only : ESMF_TimeInterval, ESMF_TimeIntervalSet, ESMF_TimeIntervalGet - use ESMF , only : ESMF_VM, ESMF_VMGet, ESMF_VMBroadcast, ESMF_VMGetCurrent - use NUOPC , only : NUOPC_CompAttributeGet - use NUOPC_Model , only : NUOPC_ModelGet - use shr_kind_mod , only : r8 => shr_kind_r8, cl=>shr_kind_cl, cs=>shr_kind_cs - use shr_sys_mod , only : shr_sys_abort - use shr_log_mod , only : shr_log_setlogunit, shr_log_getLogUnit - - implicit none - private - - public :: memcheck - public :: get_component_instance - public :: set_component_logging - public :: log_clock_advance - public :: state_getscalar - public :: state_setscalar - public :: state_diagnose - public :: alarmInit - public :: chkerr - - private :: timeInit - private :: field_getfldptr - - ! Clock and alarm options - character(len=*), private, parameter :: & - optNONE = "none" , & - optNever = "never" , & - optNSteps = "nsteps" , & - optNStep = "nstep" , & - optNSeconds = "nseconds" , & - optNSecond = "nsecond" , & - optNMinutes = "nminutes" , & - optNMinute = "nminute" , & - optNHours = "nhours" , & - optNHour = "nhour" , & - optNDays = "ndays" , & - optNDay = "nday" , & - optNMonths = "nmonths" , & - optNMonth = "nmonth" , & - optNYears = "nyears" , & - optNYear = "nyear" , & - optMonthly = "monthly" , & - optYearly = "yearly" , & - optDate = "date" , & - optIfdays0 = "ifdays0" - - ! Module data - integer, parameter :: SecPerDay = 86400 ! Seconds per day - integer, parameter :: memdebug_level=1 - character(len=1024) :: msgString - character(len=*), parameter :: u_FILE_u = & - __FILE__ - -!=============================================================================== -contains -!=============================================================================== - - subroutine memcheck(string, level, mastertask) - - ! input/output variables - character(len=*) , intent(in) :: string - integer , intent(in) :: level - logical , intent(in) :: mastertask - - ! local variables - integer :: ierr - integer, external :: GPTLprint_memusage - !----------------------------------------------------------------------- - - if ((mastertask .and. memdebug_level > level) .or. memdebug_level > level+1) then - ierr = GPTLprint_memusage(string) - endif - - end subroutine memcheck - -!=============================================================================== - - subroutine get_component_instance(gcomp, inst_suffix, inst_index, rc) - - ! input/output variables - type(ESMF_GridComp) :: gcomp - character(len=*) , intent(out) :: inst_suffix - integer , intent(out) :: inst_index - integer , intent(out) :: rc - - ! local variables - logical :: isPresent - character(len=4) :: cvalue - !----------------------------------------------------------------------- - - rc = ESMF_SUCCESS - - call NUOPC_CompAttributeGet(gcomp, name="inst_suffix", isPresent=isPresent, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - if (isPresent) then - call NUOPC_CompAttributeGet(gcomp, name="inst_suffix", value=inst_suffix, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - cvalue = inst_suffix(2:) - read(cvalue, *) inst_index - else - inst_suffix = "" - inst_index=1 - endif - - end subroutine get_component_instance - -!=============================================================================== - - subroutine set_component_logging(gcomp, mastertask, logunit, shrlogunit, rc) - use ESMF, only : ESMF_GridCompGet, ESMF_LogWrite - use NUOPC, only: NUOPC_CompAttributeAdd, NUOPC_CompAttributeSet - ! input/output variables - type(ESMF_GridComp) :: gcomp - logical, intent(in) :: mastertask - integer, intent(out) :: logunit - integer, intent(out) :: shrlogunit - integer, intent(out) :: rc - - ! local variables - character(len=CL) :: diro, name - character(len=CL) :: logfile - character(len=*), parameter :: subname ='('//__FILE__//': set_component_logging)' - !----------------------------------------------------------------------- - - rc = ESMF_SUCCESS - - shrlogunit = 6 - - if (mastertask) then - call NUOPC_CompAttributeGet(gcomp, name="diro", value=diro, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - call NUOPC_CompAttributeGet(gcomp, name="logfile", value=logfile, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - open(newunit=logunit,file=trim(diro)//"/"//trim(logfile)) - else - logUnit = 6 - endif - - call shr_log_setLogUnit (logunit) - - call ESMF_GridCompGet(gcomp, name=name, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call ESMF_LogWrite(trim(subname)//": setting logunit for component: "//trim(name), ESMF_LOGMSG_INFO) - - call NUOPC_CompAttributeAdd(gcomp, attrList=(/'logunit'/), rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - call NUOPC_CompAttributeSet(gcomp, name='logunit',value=logunit, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - - end subroutine set_component_logging - -!=============================================================================== - - subroutine log_clock_advance(clock, component, logunit, rc) - - ! input/output variables - type(ESMF_Clock) :: clock - character(len=*) , intent(in) :: component - integer , intent(in) :: logunit - integer , intent(out) :: rc - - ! local variables - character(len=CL) :: cvalue, prestring - !----------------------------------------------------------------------- - - rc = ESMF_SUCCESS - - write(prestring, *) "------>Advancing ",trim(component)," from: " - call ESMF_ClockPrint(clock, options="currTime", unit=cvalue, preString=trim(prestring), rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - write(logunit, *) trim(cvalue) - - call ESMF_ClockPrint(clock, options="stopTime", unit=cvalue, & - preString="--------------------------------> to: ", rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - write(logunit, *) trim(cvalue) - - end subroutine log_clock_advance - -!=============================================================================== - - subroutine state_getscalar(state, scalar_id, scalar_value, flds_scalar_name, flds_scalar_num, rc) - - ! ---------------------------------------------- - ! Get scalar data from State for a particular name and broadcast it to all other pets - ! ---------------------------------------------- - - ! input/output variables - type(ESMF_State), intent(in) :: state - integer, intent(in) :: scalar_id - real(r8), intent(out) :: scalar_value - character(len=*), intent(in) :: flds_scalar_name - integer, intent(in) :: flds_scalar_num - integer, intent(inout) :: rc - - ! local variables - integer :: mytask, ierr, len - type(ESMF_VM) :: vm - type(ESMF_Field) :: field - real(r8), pointer :: farrayptr(:,:) - real(r8) :: tmp(1) - character(len=*), parameter :: subname='(state_getscalar)' - ! ---------------------------------------------- - - rc = ESMF_SUCCESS - - call ESMF_VMGetCurrent(vm, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call ESMF_VMGet(vm, localPet=mytask, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call ESMF_StateGet(State, itemName=trim(flds_scalar_name), field=field, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - if (mytask == 0) then - call ESMF_FieldGet(field, farrayPtr = farrayptr, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - if (scalar_id < 0 .or. scalar_id > flds_scalar_num) then - call ESMF_LogWrite(trim(subname)//": ERROR in scalar_id", ESMF_LOGMSG_INFO, line=__LINE__, file=u_FILE_u) - rc = ESMF_FAILURE - if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=u_FILE_u)) return - endif - tmp(:) = farrayptr(scalar_id,:) - endif - call ESMF_VMBroadCast(vm, tmp, 1, 0, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - scalar_value = tmp(1) - - end subroutine state_getscalar - -!================================================================================ - - subroutine state_setscalar(scalar_value, scalar_id, State, flds_scalar_name, flds_scalar_num, rc) - - ! ---------------------------------------------- - ! Set scalar data from State for a particular name - ! ---------------------------------------------- - - ! input/output arguments - real(r8), intent(in) :: scalar_value - integer, intent(in) :: scalar_id - type(ESMF_State), intent(inout) :: State - character(len=*), intent(in) :: flds_scalar_name - integer, intent(in) :: flds_scalar_num - integer, intent(inout) :: rc - - ! local variables - integer :: mytask - type(ESMF_Field) :: lfield - type(ESMF_VM) :: vm - real(r8), pointer :: farrayptr(:,:) - character(len=*), parameter :: subname='(state_setscalar)' - ! ---------------------------------------------- - - rc = ESMF_SUCCESS - - call ESMF_VMGetCurrent(vm, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call ESMF_VMGet(vm, localPet=mytask, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call ESMF_StateGet(State, itemName=trim(flds_scalar_name), field=lfield, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - if (mytask == 0) then - call ESMF_FieldGet(lfield, farrayPtr = farrayptr, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - if (scalar_id < 0 .or. scalar_id > flds_scalar_num) then - call ESMF_LogWrite(trim(subname)//": ERROR in scalar_id", ESMF_LOGMSG_INFO) - rc = ESMF_FAILURE - return - endif - farrayptr(scalar_id,1) = scalar_value - endif - - end subroutine state_setscalar - -!=============================================================================== - - subroutine state_diagnose(State, string, rc) - - ! ---------------------------------------------- - ! Diagnose status of State - ! ---------------------------------------------- - - type(ESMF_State), intent(in) :: state - character(len=*), intent(in) :: string - integer , intent(out) :: rc - - ! local variables - integer :: i,j,n - type(ESMf_Field) :: lfield - integer :: fieldCount, lrank - character(ESMF_MAXSTR) ,pointer :: lfieldnamelist(:) - real(r8), pointer :: dataPtr1d(:) - real(r8), pointer :: dataPtr2d(:,:) - character(len=*),parameter :: subname='(state_diagnose)' - ! ---------------------------------------------- - - call ESMF_StateGet(state, itemCount=fieldCount, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - allocate(lfieldnamelist(fieldCount)) - - call ESMF_StateGet(state, itemNameList=lfieldnamelist, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - do n = 1, fieldCount - - call ESMF_StateGet(state, itemName=lfieldnamelist(n), field=lfield, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call field_getfldptr(lfield, fldptr1=dataPtr1d, fldptr2=dataPtr2d, rank=lrank, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - if (lrank == 0) then - ! no local data - elseif (lrank == 1) then - if (size(dataPtr1d) > 0) then - write(msgString,'(A,3g14.7,i8)') trim(string)//': '//trim(lfieldnamelist(n)), & - minval(dataPtr1d), maxval(dataPtr1d), sum(dataPtr1d), size(dataPtr1d) - else - write(msgString,'(A,a)') trim(string)//': '//trim(lfieldnamelist(n))," no data" - endif - elseif (lrank == 2) then - if (size(dataPtr2d) > 0) then - write(msgString,'(A,3g14.7,i8)') trim(string)//': '//trim(lfieldnamelist(n)), & - minval(dataPtr2d), maxval(dataPtr2d), sum(dataPtr2d), size(dataPtr2d) - else - write(msgString,'(A,a)') trim(string)//': '//trim(lfieldnamelist(n))," no data" - endif - else - call ESMF_LogWrite(trim(subname)//": ERROR rank not supported ", ESMF_LOGMSG_ERROR) - rc = ESMF_FAILURE - return - endif - call ESMF_LogWrite(trim(msgString), ESMF_LOGMSG_INFO) - enddo - - deallocate(lfieldnamelist) - - end subroutine state_diagnose - -!=============================================================================== - - subroutine field_getfldptr(field, fldptr1, fldptr2, rank, abort, rc) - - ! ---------------------------------------------- - ! for a field, determine rank and return fldptr1 or fldptr2 - ! abort is true by default and will abort if fldptr is not yet allocated in field - ! rank returns 0, 1, or 2. 0 means fldptr not allocated and abort=false - ! ---------------------------------------------- - - ! input/output variables - type(ESMF_Field) , intent(in) :: field - real(r8), pointer , intent(inout), optional :: fldptr1(:) - real(r8), pointer , intent(inout), optional :: fldptr2(:,:) - integer , intent(out) , optional :: rank - logical , intent(in) , optional :: abort - integer , intent(out) , optional :: rc - - ! local variables - type(ESMF_GeomType_Flag) :: geomtype - type(ESMF_FieldStatus_Flag) :: status - type(ESMF_Mesh) :: lmesh - integer :: lrank, nnodes, nelements - logical :: labort - character(len=*), parameter :: subname='(field_getfldptr)' - ! ---------------------------------------------- - - if (.not.present(rc)) then - call ESMF_LogWrite(trim(subname)//": ERROR rc not present ", & - ESMF_LOGMSG_ERROR, line=__LINE__, file=u_FILE_u) - rc = ESMF_FAILURE - return - endif - - rc = ESMF_SUCCESS - - labort = .true. - if (present(abort)) then - labort = abort - endif - lrank = -99 - - call ESMF_FieldGet(field, status=status, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - if (status /= ESMF_FIELDSTATUS_COMPLETE) then - lrank = 0 - if (labort) then - call ESMF_LogWrite(trim(subname)//": ERROR data not allocated ", ESMF_LOGMSG_INFO, rc=rc) - rc = ESMF_FAILURE - return - else - call ESMF_LogWrite(trim(subname)//": WARNING data not allocated ", ESMF_LOGMSG_INFO, rc=rc) - endif - else - - call ESMF_FieldGet(field, geomtype=geomtype, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - if (geomtype == ESMF_GEOMTYPE_GRID) then - call ESMF_FieldGet(field, rank=lrank, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - elseif (geomtype == ESMF_GEOMTYPE_MESH) then - call ESMF_FieldGet(field, rank=lrank, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - call ESMF_FieldGet(field, mesh=lmesh, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - call ESMF_MeshGet(lmesh, numOwnedNodes=nnodes, numOwnedElements=nelements, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - if (nnodes == 0 .and. nelements == 0) lrank = 0 - else - call ESMF_LogWrite(trim(subname)//": ERROR geomtype not supported ", & - ESMF_LOGMSG_INFO, rc=rc) - rc = ESMF_FAILURE - return - endif ! geomtype - - if (lrank == 0) then - call ESMF_LogWrite(trim(subname)//": no local nodes or elements ", & - ESMF_LOGMSG_INFO) - elseif (lrank == 1) then - if (.not.present(fldptr1)) then - call ESMF_LogWrite(trim(subname)//": ERROR missing rank=1 array ", & - ESMF_LOGMSG_ERROR, line=__LINE__, file=u_FILE_u) - rc = ESMF_FAILURE - return - endif - call ESMF_FieldGet(field, farrayPtr=fldptr1, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - elseif (lrank == 2) then - if (.not.present(fldptr2)) then - call ESMF_LogWrite(trim(subname)//": ERROR missing rank=2 array ", & - ESMF_LOGMSG_ERROR, line=__LINE__, file=u_FILE_u) - rc = ESMF_FAILURE - return - endif - call ESMF_FieldGet(field, farrayPtr=fldptr2, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - else - call ESMF_LogWrite(trim(subname)//": ERROR in rank ", & - ESMF_LOGMSG_ERROR, line=__LINE__, file=u_FILE_u) - rc = ESMF_FAILURE - return - endif - - endif ! status - - if (present(rank)) then - rank = lrank - endif - - end subroutine field_getfldptr - -!=============================================================================== - - subroutine alarmInit( clock, alarm, option, & - opt_n, opt_ymd, opt_tod, RefTime, alarmname, rc) - - ! Setup an alarm in a clock - ! Notes: The ringtime sent to AlarmCreate MUST be the next alarm - ! time. If you send an arbitrary but proper ringtime from the - ! past and the ring interval, the alarm will always go off on the - ! next clock advance and this will cause serious problems. Even - ! if it makes sense to initialize an alarm with some reference - ! time and the alarm interval, that reference time has to be - ! advance forward to be >= the current time. In the logic below - ! we set an appropriate "NextAlarm" and then we make sure to - ! advance it properly based on the ring interval. - - ! input/output variables - type(ESMF_Clock) , intent(inout) :: clock ! clock - type(ESMF_Alarm) , intent(inout) :: alarm ! alarm - character(len=*) , intent(in) :: option ! alarm option - integer , optional , intent(in) :: opt_n ! alarm freq - integer , optional , intent(in) :: opt_ymd ! alarm ymd - integer , optional , intent(in) :: opt_tod ! alarm tod (sec) - type(ESMF_Time) , optional , intent(in) :: RefTime ! ref time - character(len=*) , optional , intent(in) :: alarmname ! alarm name - integer , intent(inout) :: rc ! Return code - - ! local variables - type(ESMF_Calendar) :: cal ! calendar - integer :: lymd ! local ymd - integer :: ltod ! local tod - integer :: cyy,cmm,cdd,csec ! time info - character(len=64) :: lalarmname ! local alarm name - logical :: update_nextalarm ! update next alarm - type(ESMF_Time) :: CurrTime ! Current Time - type(ESMF_Time) :: NextAlarm ! Next restart alarm time - type(ESMF_TimeInterval) :: AlarmInterval ! Alarm interval - integer :: sec - character(len=*), parameter :: subname = '(set_alarmInit): ' - !------------------------------------------------------------------------------- - - rc = ESMF_SUCCESS - - lalarmname = 'alarm_unknown' - if (present(alarmname)) lalarmname = trim(alarmname) - ltod = 0 - if (present(opt_tod)) ltod = opt_tod - lymd = -1 - if (present(opt_ymd)) lymd = opt_ymd - - call ESMF_ClockGet(clock, CurrTime=CurrTime, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call ESMF_TimeGet(CurrTime, yy=cyy, mm=cmm, dd=cdd, s=csec, rc=rc ) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! initial guess of next alarm, this will be updated below - if (present(RefTime)) then - NextAlarm = RefTime - else - NextAlarm = CurrTime - endif - - ! Determine calendar - call ESMF_ClockGet(clock, calendar=cal) - - ! Determine inputs for call to create alarm - selectcase (trim(option)) - - case (optNONE) - call ESMF_TimeIntervalSet(AlarmInterval, yy=9999, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - call ESMF_TimeSet( NextAlarm, yy=9999, mm=12, dd=1, s=0, calendar=cal, rc=rc ) - if (chkerr(rc,__LINE__,u_FILE_u)) return - update_nextalarm = .false. - - case (optNever) - call ESMF_TimeIntervalSet(AlarmInterval, yy=9999, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - call ESMF_TimeSet( NextAlarm, yy=9999, mm=12, dd=1, s=0, calendar=cal, rc=rc ) - if (chkerr(rc,__LINE__,u_FILE_u)) return - update_nextalarm = .false. - - case (optDate) - if (.not. present(opt_ymd)) then - call shr_sys_abort(subname//trim(option)//' requires opt_ymd') - end if - if (lymd < 0 .or. ltod < 0) then - call shr_sys_abort(subname//trim(option)//'opt_ymd, opt_tod invalid') - end if - call ESMF_TimeIntervalSet(AlarmInterval, yy=9999, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - call timeInit(NextAlarm, lymd, cal, ltod, rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - update_nextalarm = .false. - - case (optIfdays0) - if (.not. present(opt_ymd)) then - call shr_sys_abort(subname//trim(option)//' requires opt_ymd') - end if - if (.not.present(opt_n)) then - call shr_sys_abort(subname//trim(option)//' requires opt_n') - end if - if (opt_n <= 0) then - call shr_sys_abort(subname//trim(option)//' invalid opt_n') - end if - call ESMF_TimeIntervalSet(AlarmInterval, mm=1, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - call ESMF_TimeSet( NextAlarm, yy=cyy, mm=cmm, dd=opt_n, s=0, calendar=cal, rc=rc ) - if (chkerr(rc,__LINE__,u_FILE_u)) return - update_nextalarm = .true. - - case (optNSteps) - if (.not.present(opt_n)) then - call shr_sys_abort(subname//trim(option)//' requires opt_n') - end if - if (opt_n <= 0) then - call shr_sys_abort(subname//trim(option)//' invalid opt_n') - end if - call ESMF_ClockGet(clock, TimeStep=AlarmInterval, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - AlarmInterval = AlarmInterval * opt_n - update_nextalarm = .true. - - case (optNStep) - if (.not.present(opt_n)) call shr_sys_abort(subname//trim(option)//' requires opt_n') - if (opt_n <= 0) call shr_sys_abort(subname//trim(option)//' invalid opt_n') - call ESMF_ClockGet(clock, TimeStep=AlarmInterval, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - AlarmInterval = AlarmInterval * opt_n - update_nextalarm = .true. - - case (optNSeconds) - if (.not.present(opt_n)) then - call shr_sys_abort(subname//trim(option)//' requires opt_n') - end if - if (opt_n <= 0) then - call shr_sys_abort(subname//trim(option)//' invalid opt_n') - end if - call ESMF_TimeIntervalSet(AlarmInterval, s=1, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - AlarmInterval = AlarmInterval * opt_n - update_nextalarm = .true. - - case (optNSecond) - if (.not.present(opt_n)) then - call shr_sys_abort(subname//trim(option)//' requires opt_n') - end if - if (opt_n <= 0) then - call shr_sys_abort(subname//trim(option)//' invalid opt_n') - end if - call ESMF_TimeIntervalSet(AlarmInterval, s=1, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - AlarmInterval = AlarmInterval * opt_n - update_nextalarm = .true. - - case (optNMinutes) - call ESMF_TimeIntervalSet(AlarmInterval, s=60, rc=rc) - if (.not.present(opt_n)) then - call shr_sys_abort(subname//trim(option)//' requires opt_n') - end if - if (opt_n <= 0) then - call shr_sys_abort(subname//trim(option)//' invalid opt_n') - end if - AlarmInterval = AlarmInterval * opt_n - update_nextalarm = .true. - - case (optNMinute) - if (.not.present(opt_n)) then - call shr_sys_abort(subname//trim(option)//' requires opt_n') - end if - if (opt_n <= 0) then - call shr_sys_abort(subname//trim(option)//' invalid opt_n') - end if - call ESMF_TimeIntervalSet(AlarmInterval, s=60, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - AlarmInterval = AlarmInterval * opt_n - update_nextalarm = .true. - - case (optNHours) - if (.not.present(opt_n)) then - call shr_sys_abort(subname//trim(option)//' requires opt_n') - end if - if (opt_n <= 0) then - call shr_sys_abort(subname//trim(option)//' invalid opt_n') - end if - call ESMF_TimeIntervalSet(AlarmInterval, s=3600, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - AlarmInterval = AlarmInterval * opt_n - update_nextalarm = .true. - - case (optNHour) - if (.not.present(opt_n)) then - call shr_sys_abort(subname//trim(option)//' requires opt_n') - end if - if (opt_n <= 0) then - call shr_sys_abort(subname//trim(option)//' invalid opt_n') - end if - call ESMF_TimeIntervalSet(AlarmInterval, s=3600, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - AlarmInterval = AlarmInterval * opt_n - update_nextalarm = .true. - - case (optNDays) - if (.not.present(opt_n)) then - call shr_sys_abort(subname//trim(option)//' requires opt_n') - end if - if (opt_n <= 0) then - call shr_sys_abort(subname//trim(option)//' invalid opt_n') - end if - call ESMF_TimeIntervalSet(AlarmInterval, d=1, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - AlarmInterval = AlarmInterval * opt_n - update_nextalarm = .true. - - case (optNDay) - if (.not.present(opt_n)) then - call shr_sys_abort(subname//trim(option)//' requires opt_n') - end if - if (opt_n <= 0) then - call shr_sys_abort(subname//trim(option)//' invalid opt_n') - end if - call ESMF_TimeIntervalSet(AlarmInterval, d=1, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - AlarmInterval = AlarmInterval * opt_n - update_nextalarm = .true. - - case (optNMonths) - if (.not.present(opt_n)) then - call shr_sys_abort(subname//trim(option)//' requires opt_n') - end if - if (opt_n <= 0) then - call shr_sys_abort(subname//trim(option)//' invalid opt_n') - end if - call ESMF_TimeIntervalSet(AlarmInterval, mm=1, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - AlarmInterval = AlarmInterval * opt_n - update_nextalarm = .true. - - case (optNMonth) - if (.not.present(opt_n)) then - call shr_sys_abort(subname//trim(option)//' requires opt_n') - end if - if (opt_n <= 0) then - call shr_sys_abort(subname//trim(option)//' invalid opt_n') - end if - call ESMF_TimeIntervalSet(AlarmInterval, mm=1, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - AlarmInterval = AlarmInterval * opt_n - update_nextalarm = .true. - - case (optMonthly) - call ESMF_TimeIntervalSet(AlarmInterval, mm=1, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - call ESMF_TimeSet( NextAlarm, yy=cyy, mm=cmm, dd=1, s=0, calendar=cal, rc=rc ) - if (chkerr(rc,__LINE__,u_FILE_u)) return - update_nextalarm = .true. - - case (optNYears) - if (.not.present(opt_n)) then - call shr_sys_abort(subname//trim(option)//' requires opt_n') - end if - if (opt_n <= 0) then - call shr_sys_abort(subname//trim(option)//' invalid opt_n') - end if - call ESMF_TimeIntervalSet(AlarmInterval, yy=1, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - AlarmInterval = AlarmInterval * opt_n - update_nextalarm = .true. - - case (optNYear) - if (.not.present(opt_n)) then - call shr_sys_abort(subname//trim(option)//' requires opt_n') - end if - if (opt_n <= 0) then - call shr_sys_abort(subname//trim(option)//' invalid opt_n') - end if - call ESMF_TimeIntervalSet(AlarmInterval, yy=1, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - AlarmInterval = AlarmInterval * opt_n - update_nextalarm = .true. - - case (optYearly) - call ESMF_TimeIntervalSet(AlarmInterval, yy=1, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - call ESMF_TimeSet( NextAlarm, yy=cyy, mm=1, dd=1, s=0, calendar=cal, rc=rc ) - if (chkerr(rc,__LINE__,u_FILE_u)) return - update_nextalarm = .true. - - case default - call shr_sys_abort(subname//'unknown option '//trim(option)) - - end select - - ! -------------------------------------------------------------------------------- - ! --- AlarmInterval and NextAlarm should be set --- - ! -------------------------------------------------------------------------------- - - ! --- advance Next Alarm so it won't ring on first timestep for - ! --- most options above. go back one alarminterval just to be careful - - if (update_nextalarm) then - NextAlarm = NextAlarm - AlarmInterval - do while (NextAlarm <= CurrTime) - NextAlarm = NextAlarm + AlarmInterval - enddo - endif - - alarm = ESMF_AlarmCreate( name=lalarmname, clock=clock, ringTime=NextAlarm, & - ringInterval=AlarmInterval, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - end subroutine alarmInit - -!=============================================================================== - - subroutine timeInit( Time, ymd, cal, tod, rc) - - ! Create the ESMF_Time object corresponding to the given input time, - ! given in YMD (Year Month Day) and TOD (Time-of-day) format. - ! Set the time by an integer as YYYYMMDD and integer seconds in the day - - ! input/output parameters: - type(ESMF_Time) , intent(inout) :: Time ! ESMF time - integer , intent(in) :: ymd ! year, month, day YYYYMMDD - type(ESMF_Calendar) , intent(in) :: cal ! ESMF calendar - integer , intent(in) :: tod ! time of day in seconds - integer , intent(out) :: rc - - ! local variables - integer :: year, mon, day ! year, month, day as integers - integer :: tdate ! temporary date - integer :: date ! coded-date (yyyymmdd) - character(len=*), parameter :: subname='(timeInit)' - !------------------------------------------------------------------------------- - - rc = ESMF_SUCCESS - - if ( (ymd < 0) .or. (tod < 0) .or. (tod > SecPerDay) )then - call shr_sys_abort( subname//'ERROR yymmdd is a negative number or time-of-day out of bounds' ) - end if - - tdate = abs(date) - year = int(tdate/10000) - if (date < 0) year = -year - mon = int( mod(tdate,10000)/ 100) - day = mod(tdate, 100) - - call ESMF_TimeSet( Time, yy=year, mm=mon, dd=day, s=tod, calendar=cal, rc=rc ) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - end subroutine timeInit - -!=============================================================================== - - logical function chkerr(rc, line, file) - - integer, intent(in) :: rc - integer, intent(in) :: line - character(len=*), intent(in) :: file - - integer :: lrc - - chkerr = .false. - lrc = rc - if (ESMF_LogFoundError(rcToCheck=lrc, msg=ESMF_LOGERR_PASSTHRU, line=line, file=file)) then - chkerr = .true. - endif - end function chkerr - -!=============================================================================== - -end module dead_methods_mod diff --git a/CIME/non_py/src/components/xcpl_comps_nuopc/xshare/dead_nuopc_mod.F90 b/CIME/non_py/src/components/xcpl_comps_nuopc/xshare/dead_nuopc_mod.F90 deleted file mode 100644 index ee3ca6b682e..00000000000 --- a/CIME/non_py/src/components/xcpl_comps_nuopc/xshare/dead_nuopc_mod.F90 +++ /dev/null @@ -1,346 +0,0 @@ -module dead_nuopc_mod - - use ESMF , only : ESMF_Gridcomp, ESMF_State, ESMF_StateGet - use ESMF , only : ESMF_Clock, ESMF_Time, ESMF_TimeInterval, ESMF_Alarm - use ESMF , only : ESMF_GridCompGet, ESMF_ClockGet, ESMF_ClockSet, ESMF_ClockAdvance, ESMF_AlarmSet - use ESMF , only : ESMF_SUCCESS, ESMF_LogWrite, ESMF_LOGMSG_INFO, ESMF_METHOD_INITIALIZE - use ESMF , only : ESMF_FAILURE, ESMF_LOGMSG_ERROR - use ESMF , only : ESMF_VMGetCurrent, ESMF_VM, ESMF_VMBroadcast, ESMF_VMGet - use ESMF , only : ESMF_VM, ESMF_VMGetCurrent, ESMF_VmGet - use ESMF , only : operator(/=), operator(==), operator(+) - use shr_kind_mod , only : r8=>shr_kind_r8, i8=>shr_kind_i8, cl=>shr_kind_cl, cs=>shr_kind_cs - use shr_sys_mod , only : shr_sys_abort - use dead_methods_mod , only : chkerr, alarmInit - - implicit none - private - - public :: dead_read_inparms - public :: ModelInitPhase - public :: ModelSetRunClock - public :: fld_list_add - public :: fld_list_realize - - ! !PUBLIC DATA MEMBERS: - type fld_list_type - character(len=128) :: stdname - integer :: ungridded_lbound = 0 - integer :: ungridded_ubound = 0 - end type fld_list_type - public :: fld_list_type - - integer, parameter, public :: fldsMax = 100 - integer :: dbug_flag = 0 - character(*), parameter :: u_FILE_u = & - __FILE__ - -!=============================================================================== -contains -!=============================================================================== - - subroutine dead_read_inparms(model, inst_suffix, logunit, nxg, nyg) - - ! input/output variables - character(len=*) , intent(in) :: model - character(len=*) , intent(in) :: inst_suffix ! char string associated with instance - integer , intent(in) :: logunit ! logging unit number - integer , intent(out) :: nxg ! global dim i-direction - integer , intent(out) :: nyg ! global dim j-direction - - ! local variables - type(ESMF_VM) :: vm - character(CL) :: fileName ! generic file name - integer :: nunit ! unit number - integer :: unitn ! Unit for namelist file - integer :: tmp(2) ! array for broadcast - integer :: localPet ! mpi id of current task in current context - integer :: rc ! return code - character(*), parameter :: F00 = "('(dead_read_inparms) ',8a)" - character(*), parameter :: F01 = "('(dead_read_inparms) ',a,a,4i8)" - character(*), parameter :: F03 = "('(dead_read_inparms) ',a,a,i8,a)" - character(*), parameter :: subName = "(dead_read_inpamrs) " - !------------------------------------------------------------------------------- - - ! read the input parms (used to configure model) - call ESMF_VMGetCurrent(vm, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - call ESMF_VMGet(vm, localPet=localPet, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - nxg = -9999 - nyg = -9999 - - if (localPet==0) then - open(newunit=unitn, file='x'//model//'_in'//trim(inst_suffix), status='old' ) - read(unitn,*) nxg - read(unitn,*) nyg - close (unitn) - endif - - tmp(1) = nxg - tmp(2) = nyg - call ESMF_VMBroadcast(vm, tmp, 3, 0, rc=rc) - nxg = tmp(1) - nyg = tmp(2) - - if (localPet==0) then - write(logunit,*)' Read in X'//model//' input from file= x'//model//'_in' - write(logunit,F00) model - write(logunit,F00) model,' Model : ',model - write(logunit,F01) model,' NGX : ',nxg - write(logunit,F01) model,' NGY : ',nyg - write(logunit,F00) model,' inst_suffix : ',trim(inst_suffix) - write(logunit,F00) model - end if - - end subroutine dead_read_inparms - - !=============================================================================== - subroutine fld_list_add(num, fldlist, stdname, ungridded_lbound, ungridded_ubound) - - ! input/output variables - integer , intent(inout) :: num - type(fld_list_type) , intent(inout) :: fldlist(:) - character(len=*) , intent(in) :: stdname - integer, optional , intent(in) :: ungridded_lbound - integer, optional , intent(in) :: ungridded_ubound - - ! local variables - character(len=*), parameter :: subname='(dead_nuopc_mod:fld_list_add)' - !------------------------------------------------------------------------------- - - ! Set up a list of field information - num = num + 1 - if (num > fldsMax) then - call ESMF_LogWrite(trim(subname)//": ERROR num > fldsMax "//trim(stdname), & - ESMF_LOGMSG_ERROR, line=__LINE__, file=__FILE__) - return - endif - fldlist(num)%stdname = trim(stdname) - - if (present(ungridded_lbound) .and. present(ungridded_ubound)) then - fldlist(num)%ungridded_lbound = ungridded_lbound - fldlist(num)%ungridded_ubound = ungridded_ubound - end if - - end subroutine fld_list_add - - !=============================================================================== - subroutine fld_list_realize(state, fldList, numflds, flds_scalar_name, flds_scalar_num, mesh, tag, rc) - - use NUOPC , only : NUOPC_IsConnected, NUOPC_Realize - use ESMF , only : ESMF_MeshLoc_Element, ESMF_FieldCreate, ESMF_TYPEKIND_R8 - use ESMF , only : ESMF_MAXSTR, ESMF_Field, ESMF_State, ESMF_Mesh, ESMF_StateRemove - use ESMF , only : ESMF_LogFoundError, ESMF_LOGMSG_INFO, ESMF_SUCCESS - use ESMF , only : ESMF_LogWrite, ESMF_LOGMSG_ERROR, ESMF_LOGERR_PASSTHRU - - type(ESMF_State) , intent(inout) :: state - type(fld_list_type) , intent(in) :: fldList(:) - integer , intent(in) :: numflds - character(len=*) , intent(in) :: flds_scalar_name - integer , intent(in) :: flds_scalar_num - character(len=*) , intent(in) :: tag - type(ESMF_Mesh) , intent(in) :: mesh - integer , intent(inout) :: rc - - ! local variables - integer :: n - type(ESMF_Field) :: field - character(len=80) :: stdname - integer :: gridtoFieldMap=2 - character(len=*),parameter :: subname='(dead_nuopc_mod:fld_list_realize)' - ! ---------------------------------------------- - - rc = ESMF_SUCCESS - - do n = 1, numflds - stdname = fldList(n)%stdname - if (NUOPC_IsConnected(state, fieldName=stdname)) then - if (stdname == trim(flds_scalar_name)) then - call ESMF_LogWrite(trim(subname)//trim(tag)//" Field = "//trim(stdname)//" is connected on root pe", & - ESMF_LOGMSG_INFO) - ! Create the scalar field - call SetScalarField(field, flds_scalar_name, flds_scalar_num, rc=rc) - if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=u_FILE_u)) return - else - call ESMF_LogWrite(trim(subname)//trim(tag)//" Field = "//trim(stdname)//" is connected using mesh", & - ESMF_LOGMSG_INFO) - ! Create the field - if (fldlist(n)%ungridded_lbound > 0 .and. fldlist(n)%ungridded_ubound > 0) then - field = ESMF_FieldCreate(mesh, ESMF_TYPEKIND_R8, name=stdname, meshloc=ESMF_MESHLOC_ELEMENT, & - ungriddedLbound=(/fldlist(n)%ungridded_lbound/), & - ungriddedUbound=(/fldlist(n)%ungridded_ubound/), & - gridToFieldMap=(/gridToFieldMap/), rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - else - field = ESMF_FieldCreate(mesh, ESMF_TYPEKIND_R8, name=stdname, meshloc=ESMF_MESHLOC_ELEMENT, rc=rc) - if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=u_FILE_u)) return - end if - endif - - ! NOW call NUOPC_Realize - call NUOPC_Realize(state, field=field, rc=rc) - if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=u_FILE_u)) return - else - if (stdname /= trim(flds_scalar_name)) then - call ESMF_LogWrite(subname // trim(tag) // " Field = "// trim(stdname) // " is not connected.", & - ESMF_LOGMSG_INFO) - call ESMF_StateRemove(state, (/stdname/), rc=rc) - if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=u_FILE_u)) return - end if - end if - end do - - contains !- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - subroutine SetScalarField(field, flds_scalar_name, flds_scalar_num, rc) - ! ---------------------------------------------- - ! create a field with scalar data on the root pe - ! ---------------------------------------------- - - use ESMF, only : ESMF_Field, ESMF_DistGrid, ESMF_Grid - use ESMF, only : ESMF_DistGridCreate, ESMF_GridCreate, ESMF_LogFoundError, ESMF_LOGERR_PASSTHRU - use ESMF, only : ESMF_FieldCreate, ESMF_GridCreate, ESMF_TYPEKIND_R8 - - type(ESMF_Field) , intent(inout) :: field - character(len=*) , intent(in) :: flds_scalar_name - integer , intent(in) :: flds_scalar_num - integer , intent(inout) :: rc - - ! local variables - type(ESMF_Distgrid) :: distgrid - type(ESMF_Grid) :: grid - character(len=*), parameter :: subname='(dead_nuopc_mod:SetScalarField)' - ! ---------------------------------------------- - - rc = ESMF_SUCCESS - - ! create a DistGrid with a single index space element, which gets mapped onto DE 0. - distgrid = ESMF_DistGridCreate(minIndex=(/1/), maxIndex=(/1/), rc=rc) - if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=u_FILE_u)) return - - grid = ESMF_GridCreate(distgrid, rc=rc) - if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=u_FILE_u)) return - - field = ESMF_FieldCreate(name=trim(flds_scalar_name), grid=grid, typekind=ESMF_TYPEKIND_R8, & - ungriddedLBound=(/1/), ungriddedUBound=(/flds_scalar_num/), gridToFieldMap=(/2/), rc=rc) - if (ESMF_LogFoundError(rcToCheck=rc, msg=ESMF_LOGERR_PASSTHRU, line=__LINE__, file=u_FILE_u)) return - - end subroutine SetScalarField - - end subroutine fld_list_realize - - !=============================================================================== - subroutine ModelInitPhase(gcomp, importState, exportState, clock, rc) - - use NUOPC, only : NUOPC_CompFilterPhaseMap - - type(ESMF_GridComp) :: gcomp - type(ESMF_State) :: importState, exportState - type(ESMF_Clock) :: clock - integer, intent(out) :: rc - !------------------------------------------------------------------------------- - - rc = ESMF_SUCCESS - - ! Switch to IPDv01 by filtering all other phaseMap entries - call NUOPC_CompFilterPhaseMap(gcomp, ESMF_METHOD_INITIALIZE, acceptStringList=(/"IPDv01p"/), rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - end subroutine ModelInitPhase - - !=============================================================================== - subroutine ModelSetRunClock(gcomp, rc) - - use ESMF , only : ESMF_ClockGetAlarmList, ESMF_ALARMLIST_ALL - use NUOPC_Model , only : NUOPC_ModelGet - use NUOPC , only : NUOPC_CompAttributeGet - - ! input/output variables - type(ESMF_GridComp) :: gcomp - integer, intent(out) :: rc - - ! local variables - type(ESMF_Clock) :: mclock, dclock - type(ESMF_Time) :: mcurrtime, dcurrtime - type(ESMF_Time) :: mstoptime - type(ESMF_TimeInterval) :: mtimestep, dtimestep - character(len=256) :: cvalue - character(len=256) :: restart_option ! Restart option units - integer :: restart_n ! Number until restart interval - integer :: restart_ymd ! Restart date (YYYYMMDD) - type(ESMF_ALARM) :: restart_alarm - character(len=128) :: name - integer :: alarmcount - character(len=*),parameter :: subname='dead_nuopc_mod:(ModelSetRunClock) ' - !------------------------------------------------------------------------------- - - rc = ESMF_SUCCESS - - ! query the Component for its clocks - call NUOPC_ModelGet(gcomp, driverClock=dclock, modelClock=mclock, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call ESMF_ClockGet(dclock, currTime=dcurrtime, timeStep=dtimestep, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call ESMF_ClockGet(mclock, currTime=mcurrtime, timeStep=mtimestep, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - !-------------------------------- - ! force model clock currtime and timestep to match driver and set stoptime - !-------------------------------- - - mstoptime = mcurrtime + dtimestep - call ESMF_ClockSet(mclock, currTime=dcurrtime, timeStep=dtimestep, stopTime=mstoptime, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - !-------------------------------- - ! set restart alarm - !-------------------------------- - - call ESMF_ClockGetAlarmList(mclock, alarmlistflag=ESMF_ALARMLIST_ALL, alarmCount=alarmCount, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - if (alarmCount == 0) then - - call ESMF_GridCompGet(gcomp, name=name, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - call ESMF_LogWrite(subname//'setting alarms for' // trim(name), ESMF_LOGMSG_INFO) - - call NUOPC_CompAttributeGet(gcomp, name="restart_option", value=restart_option, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call NUOPC_CompAttributeGet(gcomp, name="restart_n", value=cvalue, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - read(cvalue,*) restart_n - - call NUOPC_CompAttributeGet(gcomp, name="restart_ymd", value=cvalue, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - read(cvalue,*) restart_ymd - - call alarmInit(mclock, restart_alarm, restart_option, & - opt_n = restart_n, & - opt_ymd = restart_ymd, & - RefTime = mcurrTime, & - alarmname = 'alarm_restart', rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call ESMF_AlarmSet(restart_alarm, clock=mclock, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - end if - - !-------------------------------- - ! Advance model clock to trigger alarms then reset model clock back to currtime - !-------------------------------- - - call ESMF_ClockAdvance(mclock,rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call ESMF_ClockSet(mclock, currTime=dcurrtime, timeStep=dtimestep, stopTime=mstoptime, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - end subroutine ModelSetRunClock - -end module dead_nuopc_mod diff --git a/CIME/non_py/src/components/xcpl_comps_nuopc/xwav/cime_config/buildlib b/CIME/non_py/src/components/xcpl_comps_nuopc/xwav/cime_config/buildlib deleted file mode 120000 index 7766f77f5bc..00000000000 --- a/CIME/non_py/src/components/xcpl_comps_nuopc/xwav/cime_config/buildlib +++ /dev/null @@ -1 +0,0 @@ -../../../../../../build_scripts/buildlib.internal_components \ No newline at end of file diff --git a/CIME/non_py/src/components/xcpl_comps_nuopc/xwav/cime_config/buildlib_cmake b/CIME/non_py/src/components/xcpl_comps_nuopc/xwav/cime_config/buildlib_cmake deleted file mode 120000 index 7766f77f5bc..00000000000 --- a/CIME/non_py/src/components/xcpl_comps_nuopc/xwav/cime_config/buildlib_cmake +++ /dev/null @@ -1 +0,0 @@ -../../../../../../build_scripts/buildlib.internal_components \ No newline at end of file diff --git a/CIME/non_py/src/components/xcpl_comps_nuopc/xwav/cime_config/buildnml b/CIME/non_py/src/components/xcpl_comps_nuopc/xwav/cime_config/buildnml deleted file mode 100755 index 1ea9dc3a5d8..00000000000 --- a/CIME/non_py/src/components/xcpl_comps_nuopc/xwav/cime_config/buildnml +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env python3 - -""" -build data model library -""" - -import sys, os - -_CIMEROOT = os.path.join( - os.path.dirname(os.path.abspath(__file__)), "..", "..", "..", "..", ".." -) -sys.path.append(os.path.join(_CIMEROOT, "CIME", "Tools")) - -from standard_script_setup import * -from CIME.buildnml import build_xcpl_nml, parse_input -from CIME.case import Case - - -def buildnml(case, caseroot, compname): - if compname != "xwav": - raise AttributeError - build_xcpl_nml(case, caseroot, compname) - - -def _main_func(): - caseroot = parse_input(sys.argv) - with Case(caseroot) as case: - buildnml(case, caseroot, "xwav") - - -if __name__ == "__main__": - _main_func() diff --git a/CIME/non_py/src/components/xcpl_comps_nuopc/xwav/cime_config/config_component.xml b/CIME/non_py/src/components/xcpl_comps_nuopc/xwav/cime_config/config_component.xml deleted file mode 100644 index e82944fd3d8..00000000000 --- a/CIME/non_py/src/components/xcpl_comps_nuopc/xwav/cime_config/config_component.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - Dead wave component - - - - - char - xwav - xwav - case_comp - env_case.xml - Name of wave component - - - - ========================================= - XWAV naming conventions in compset name - ========================================= - - - diff --git a/CIME/non_py/src/components/xcpl_comps_nuopc/xwav/src/wav_comp_nuopc.F90 b/CIME/non_py/src/components/xcpl_comps_nuopc/xwav/src/wav_comp_nuopc.F90 deleted file mode 100644 index aa4d982e530..00000000000 --- a/CIME/non_py/src/components/xcpl_comps_nuopc/xwav/src/wav_comp_nuopc.F90 +++ /dev/null @@ -1,465 +0,0 @@ -module wav_comp_nuopc - - !---------------------------------------------------------------------------- - ! This is the NUOPC cap for XWAV - !---------------------------------------------------------------------------- - - use ESMF - use NUOPC , only : NUOPC_CompDerive, NUOPC_CompSetEntryPoint, NUOPC_CompSpecialize - use NUOPC , only : NUOPC_CompAttributeGet, NUOPC_Advertise - use NUOPC_Model , only : model_routine_SS => SetServices - use NUOPC_Model , only : model_label_Advance => label_Advance - use NUOPC_Model , only : model_label_SetRunClock => label_SetRunClock - use NUOPC_Model , only : model_label_Finalize => label_Finalize - use NUOPC_Model , only : NUOPC_ModelGet, SetVM - use shr_sys_mod , only : shr_sys_abort - use shr_kind_mod , only : r8=>shr_kind_r8, i8=>shr_kind_i8, cl=>shr_kind_cl, cs=>shr_kind_cs - use shr_log_mod , only : shr_log_getlogunit, shr_log_setlogunit - use dead_methods_mod , only : chkerr, state_setscalar, state_diagnose, alarmInit, memcheck - use dead_methods_mod , only : set_component_logging, get_component_instance, log_clock_advance - use dead_nuopc_mod , only : dead_read_inparms, ModelInitPhase, ModelSetRunClock - use dead_nuopc_mod , only : fld_list_add, fld_list_realize, fldsMax, fld_list_type - - implicit none - private ! except - - public :: SetServices - public :: SetVM - !-------------------------------------------------------------------------- - ! Private module data - !-------------------------------------------------------------------------- - - character(len=CL) :: flds_scalar_name = '' - integer :: flds_scalar_num = 0 - integer :: flds_scalar_index_nx = 0 - integer :: flds_scalar_index_ny = 0 - integer :: flds_scalar_index_nextsw_cday = 0 - - integer :: fldsToWav_num = 0 - integer :: fldsFrWav_num = 0 - type (fld_list_type) :: fldsToWav(fldsMax) - type (fld_list_type) :: fldsFrWav(fldsMax) - integer, parameter :: gridTofieldMap = 2 ! ungridded dimension is innermost - - type(ESMF_Mesh) :: mesh - integer :: nxg ! global dim i-direction - integer :: nyg ! global dim j-direction - integer :: my_task ! my task in mpi communicator mpicom - integer :: inst_index ! number of current instance (ie. 1) - character(len=16) :: inst_suffix = "" ! char string associated with instance (ie. "_0001" or "") - integer :: logunit ! logging unit number - logical :: mastertask - integer :: dbug = 1 - character(*),parameter :: modName = "(xwav_comp_nuopc)" - character(*),parameter :: u_FILE_u = & - __FILE__ - -!=============================================================================== -contains -!=============================================================================== - - subroutine SetServices(gcomp, rc) - - type(ESMF_GridComp) :: gcomp - integer, intent(out) :: rc - character(len=*),parameter :: subname=trim(modName)//':(SetServices) ' - - rc = ESMF_SUCCESS - call ESMF_LogWrite(subname//' called', ESMF_LOGMSG_INFO, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! the NUOPC gcomp component will register the generic methods - call NUOPC_CompDerive(gcomp, model_routine_SS, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! switching to IPD versions - call ESMF_GridCompSetEntryPoint(gcomp, ESMF_METHOD_INITIALIZE, & - userRoutine=ModelInitPhase, phase=0, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! set entry point for methods that require specific implementation - call NUOPC_CompSetEntryPoint(gcomp, ESMF_METHOD_INITIALIZE, phaseLabelList=(/"IPDv01p1"/), & - userRoutine=InitializeAdvertise, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call NUOPC_CompSetEntryPoint(gcomp, ESMF_METHOD_INITIALIZE, phaseLabelList=(/"IPDv01p3"/), & - userRoutine=InitializeRealize, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! attach specializing method(s) - call NUOPC_CompSpecialize(gcomp, specLabel=model_label_Advance, specRoutine=ModelAdvance, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call ESMF_MethodRemove(gcomp, label=model_label_SetRunClock, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - call NUOPC_CompSpecialize(gcomp, specLabel=model_label_SetRunClock, specRoutine=ModelSetRunClock, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call NUOPC_CompSpecialize(gcomp, specLabel=model_label_Finalize, specRoutine=ModelFinalize, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call ESMF_LogWrite(subname//' done', ESMF_LOGMSG_INFO, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - end subroutine SetServices - - !=============================================================================== - subroutine InitializeAdvertise(gcomp, importState, exportState, clock, rc) - - ! input/output variables - type(ESMF_GridComp) :: gcomp - type(ESMF_State) :: importState, exportState - type(ESMF_Clock) :: clock - integer, intent(out) :: rc - - ! local variables - type(ESMF_VM) :: vm - character(CS) :: stdname - integer :: n - integer :: lsize ! local array size - integer :: shrlogunit ! original log unit - character(CL) :: cvalue - character(len=CL) :: logmsg - logical :: isPresent, isSet - character(len=*),parameter :: subname=trim(modName)//':(InitializeAdvertise) ' - !------------------------------------------------------------------------------- - - rc = ESMF_SUCCESS - call ESMF_LogWrite(subname//' called', ESMF_LOGMSG_INFO, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call ESMF_GridCompGet(gcomp, vm=vm, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call ESMF_VMGet(vm, localpet=my_task, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - mastertask = (my_task == 0) - - ! determine instance information - call get_component_instance(gcomp, inst_suffix, inst_index, rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! set logunit and set shr logging to my log file - call set_component_logging(gcomp, mastertask, logunit, shrlogunit, rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! Initialize xwav - call dead_read_inparms('wav', inst_suffix, logunit, nxg, nyg) - - ! advertise import and export fields - call NUOPC_CompAttributeGet(gcomp, name="ScalarFieldName", value=cvalue, isPresent=isPresent, isSet=isSet, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - if (isPresent .and. isSet) then - flds_scalar_name = trim(cvalue) - call ESMF_LogWrite(trim(subname)//' flds_scalar_name = '//trim(flds_scalar_name), ESMF_LOGMSG_INFO) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - else - call shr_sys_abort(subname//'Need to set attribute ScalarFieldName') - endif - - call NUOPC_CompAttributeGet(gcomp, name="ScalarFieldCount", value=cvalue, isPresent=isPresent, isSet=isSet, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - if (isPresent .and. isSet) then - read(cvalue, *) flds_scalar_num - write(logmsg,*) flds_scalar_num - call ESMF_LogWrite(trim(subname)//' flds_scalar_num = '//trim(logmsg), ESMF_LOGMSG_INFO) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - else - call shr_sys_abort(subname//'Need to set attribute ScalarFieldCount') - endif - - call NUOPC_CompAttributeGet(gcomp, name="ScalarFieldIdxGridNX", value=cvalue, isPresent=isPresent, isSet=isSet, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - if (isPresent .and. isSet) then - read(cvalue,*) flds_scalar_index_nx - write(logmsg,*) flds_scalar_index_nx - call ESMF_LogWrite(trim(subname)//' : flds_scalar_index_nx = '//trim(logmsg), ESMF_LOGMSG_INFO) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - else - call shr_sys_abort(subname//'Need to set attribute ScalarFieldIdxGridNX') - endif - - call NUOPC_CompAttributeGet(gcomp, name="ScalarFieldIdxGridNY", value=cvalue, isPresent=isPresent, isSet=isSet, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - if (isPresent .and. isSet) then - read(cvalue,*) flds_scalar_index_ny - write(logmsg,*) flds_scalar_index_ny - call ESMF_LogWrite(trim(subname)//' : flds_scalar_index_ny = '//trim(logmsg), ESMF_LOGMSG_INFO) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - else - call shr_sys_abort(subname//'Need to set attribute ScalarFieldIdxGridNY') - endif - - if (nxg /= 0 .and. nyg /= 0) then - - call fld_list_add(fldsFrWav_num, fldsFrWav, trim(flds_scalar_name)) - call fld_list_add(fldsFrWav_num, fldsFrWav, 'Sw_lamult' ) - call fld_list_add(fldsFrWav_num, fldsFrWav, 'Sw_ustokes' ) - call fld_list_add(fldsFrWav_num, fldsFrWav, 'Sw_vstokes' ) - call fld_list_add(fldsFrWav_num, fldsFrWav, 'Sw_hstokes' ) - - call fld_list_add(fldsToWav_num, fldsToWav, trim(flds_scalar_name)) - call fld_list_add(fldsToWav_num, fldsToWav, 'Sa_u' ) - call fld_list_add(fldsToWav_num, fldsToWav, 'Sa_v' ) - call fld_list_add(fldsToWav_num, fldsToWav, 'Sa_tbot' ) - call fld_list_add(fldsToWav_num, fldsToWav, 'Si_ifrac' ) - call fld_list_add(fldsToWav_num, fldsToWav, 'So_t' ) - call fld_list_add(fldsToWav_num, fldsToWav, 'So_u' ) - call fld_list_add(fldsToWav_num, fldsToWav, 'So_v' ) - call fld_list_add(fldsToWav_num, fldsToWav, 'So_bldepth' ) - - do n = 1,fldsFrWav_num - if (mastertask) write(logunit,*)'Advertising From Xwav ',trim(fldsFrWav(n)%stdname) - call NUOPC_Advertise(exportState, standardName=fldsFrWav(n)%stdname, & - TransferOfferGeomObject='will provide', rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - enddo - - do n = 1,fldsToWav_num - if(mastertask) write(logunit,*)'Advertising To Xwav ',trim(fldsToWav(n)%stdname) - call NUOPC_Advertise(importState, standardName=fldsToWav(n)%stdname, & - TransferOfferGeomObject='will provide', rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - enddo - end if - - call ESMF_LogWrite(subname//' done', ESMF_LOGMSG_INFO, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! Reset shr logging to original values - call shr_log_setLogUnit (shrlogunit) - - end subroutine InitializeAdvertise - - !=============================================================================== - subroutine InitializeRealize(gcomp, importState, exportState, clock, rc) - - ! input/output variables - type(ESMF_GridComp) :: gcomp - type(ESMF_State) :: importState, exportState - type(ESMF_Clock) :: clock - integer, intent(out) :: rc - - ! local variables - integer :: shrlogunit ! original log unit - character(ESMF_MAXSTR) :: cvalue ! config data - character(len=*),parameter :: subname=trim(modName)//':(InitializeRealize) ' - !------------------------------------------------------------------------------- - - rc = ESMF_SUCCESS - - ! Reset shr logging to my log file - call shr_log_getLogUnit (shrlogunit) - call shr_log_setLogUnit (logunit) - - ! generate the mesh - call NUOPC_CompAttributeGet(gcomp, name='mesh_wav', value=cvalue, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - mesh = ESMF_MeshCreate(filename=trim(cvalue), fileformat=ESMF_FILEFORMAT_ESMFMESH, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - - ! realize the actively coupled fields, now that a mesh is established - ! NUOPC_Realize "realizes" a previously advertised field in the importState and exportState - ! by replacing the advertised fields with the newly created fields of the same name. - call fld_list_realize( & - state=ExportState, & - fldlist=fldsFrWav, & - numflds=fldsFrWav_num, & - flds_scalar_name=flds_scalar_name, & - flds_scalar_num=flds_scalar_num, & - tag=subname//':dwavExport',& - mesh=mesh, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - call fld_list_realize( & - state=importState, & - fldList=fldsToWav, & - numflds=fldsToWav_num, & - flds_scalar_name=flds_scalar_name, & - flds_scalar_num=flds_scalar_num, & - tag=subname//':dwavImport',& - mesh=mesh, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! Pack export state - call State_SetExport(exportState, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - call State_SetScalar(dble(nxg),flds_scalar_index_nx, exportState, flds_scalar_name, flds_scalar_num, rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - call State_SetScalar(dble(nyg),flds_scalar_index_ny, exportState, flds_scalar_name, flds_scalar_num, rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! diagnostics - if (dbug > 1) then - call State_diagnose(exportState,subname//':ES',rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - endif - - call shr_log_setLogUnit (shrlogunit) - - end subroutine InitializeRealize - - !=============================================================================== - subroutine ModelAdvance(gcomp, rc) - - ! input/output variables - type(ESMF_GridComp) :: gcomp - integer, intent(out) :: rc - - ! local variables - type(ESMF_Clock) :: clock - type(ESMF_State) :: exportState - integer :: shrlogunit ! original log unit - character(len=*),parameter :: subname=trim(modName)//':(ModelAdvance) ' - !------------------------------------------------------------------------------- - - rc = ESMF_SUCCESS - - call memcheck(subname, 3, mastertask) - - call shr_log_getLogUnit (shrlogunit) - call shr_log_setLogUnit (logunit) - - ! Pack export state - call NUOPC_ModelGet(gcomp, modelClock=clock, exportState=exportState, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - call state_setexport(exportState, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ! diagnostics - if (dbug > 1) then - call State_diagnose(exportState,subname//':ES',rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - if ( mastertask) then - call log_clock_advance(clock, 'XWAV', logunit, rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - endif - endif - - call shr_log_setLogUnit (shrlogunit) - - end subroutine ModelAdvance - - !=============================================================================== - subroutine state_setexport(exportState, rc) - - ! input/output variables - type(ESMF_State) , intent(inout) :: exportState - integer , intent(out) :: rc - - ! local variables - integer :: nfstart, ubound - integer :: n, nf, nind - real(r8), pointer :: lat(:) - real(r8), pointer :: lon(:) - integer :: spatialDim - integer :: numOwnedElements - real(R8), pointer :: ownedElemCoords(:) - !-------------------------------------------------- - - rc = ESMF_SUCCESS - - call ESMF_MeshGet(mesh, spatialDim=spatialDim, numOwnedElements=numOwnedElements, rc=rc) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - allocate(ownedElemCoords(spatialDim*numOwnedElements)) - call ESMF_MeshGet(mesh, ownedElemCoords=ownedElemCoords) - if (ChkErr(rc,__LINE__,u_FILE_u)) return - - allocate(lon(numownedElements)) - allocate(lat(numownedElements)) - do n = 1,numownedElements - lon(n) = ownedElemCoords(2*n-1) - lat(n) = ownedElemCoords(2*n) - end do - - nfstart = 0 ! for fields that have ubound > 0 - do nf = 2,fldsFrWav_num ! Start from index 2 in order to skip the scalar field - ubound = fldsFrWav(nf)%ungridded_ubound - if (ubound == 0) then - call field_setexport(exportState, trim(fldsFrWav(nf)%stdname), lon, lat, nf=nf, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - else - nfstart = nfstart + nf + ubound - 1 - do nind = 1,ubound - call field_setexport(exportState, trim(fldsFrWav(nf)%stdname), lon, lat, nf=nfstart+nind-1, & - ungridded_index=nind, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - end do - end if - end do - - deallocate(lon) - deallocate(lat) - - end subroutine state_setexport - - !=============================================================================== - - subroutine field_setexport(exportState, fldname, lon, lat, nf, ungridded_index, rc) - - use shr_const_mod , only : pi=>shr_const_pi - - ! intput/otuput variables - type(ESMF_State) , intent(inout) :: exportState - character(len=*) , intent(in) :: fldname - real(r8) , intent(in) :: lon(:) - real(r8) , intent(in) :: lat(:) - integer , intent(in) :: nf - integer, optional , intent(in) :: ungridded_index - integer , intent(out) :: rc - - ! local variables - integer :: i, ncomp - type(ESMF_Field) :: lfield - real(r8), pointer :: data1d(:) - real(r8), pointer :: data2d(:,:) - !-------------------------------------------------- - - rc = ESMF_SUCCESS - - call ESMF_StateGet(exportState, itemName=trim(fldname), field=lfield, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - - ncomp = 7 - if (present(ungridded_index)) then - call ESMF_FieldGet(lfield, farrayPtr=data2d, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - if (gridToFieldMap == 1) then - do i = 1,size(data2d, dim=1) - data2d(i,ungridded_index) = (nf*100) * cos(pi*lat(i)/180.0_R8) * & - sin((pi*lon(i)/180.0_R8) - (ncomp-1)*(pi/3.0_R8) ) + (ncomp*10.0_R8) - end do - else if (gridToFieldMap == 2) then - do i = 1,size(data2d, dim=2) - data2d(ungridded_index,i) = (nf*100) * cos(pi*lat(i)/180.0_R8) * & - sin((pi*lon(i)/180.0_R8) - (ncomp-1)*(pi/3.0_R8) ) + (ncomp*10.0_R8) - end do - end if - else - call ESMF_FieldGet(lfield, farrayPtr=data1d, rc=rc) - if (chkerr(rc,__LINE__,u_FILE_u)) return - do i = 1,size(data1d) - data1d(i) = (nf*100) * cos(pi*lat(i)/180.0_R8) * & - sin((pi*lon(i)/180.0_R8) - (ncomp-1)*(pi/3.0_R8) ) + (ncomp*10.0_R8) - end do - end if - - end subroutine field_setexport - - !=============================================================================== - subroutine ModelFinalize(gcomp, rc) - type(ESMF_GridComp) :: gcomp - integer, intent(out) :: rc - !------------------------------------------------------------------------------- - - rc = ESMF_SUCCESS - - if (mastertask) then - write(logunit,*) - write(logunit,*) 'xwav: end of main integration loop' - write(logunit,*) - end if - end subroutine ModelFinalize - -end module wav_comp_nuopc diff --git a/CIME/non_py/src/timing/private.h b/CIME/non_py/src/timing/private.h index a255224aba5..1de5b880322 100644 --- a/CIME/non_py/src/timing/private.h +++ b/CIME/non_py/src/timing/private.h @@ -43,7 +43,7 @@ */ #define MAX_AUX 9 -#ifndef __cplusplus +#if !defined(__cplusplus) && __STDC_VERSION__ < 202311L typedef enum {false = 0, true = 1} bool; /* mimic C++ */ #endif diff --git a/CIME/scripts/configure b/CIME/scripts/configure index f31c179518c..73a326da2bf 100755 --- a/CIME/scripts/configure +++ b/CIME/scripts/configure @@ -97,7 +97,7 @@ def parse_command_line(args): argcnt = len(args) args = parser.parse_args() CIME.utils.parse_args_and_handle_standard_logging_options(args) - + machobj = None opts = {} if args.machines_dir is not None: machines_file = os.path.join(args.machines_dir, "config_machines.xml") diff --git a/CIME/test_scheduler.py b/CIME/test_scheduler.py index 2f05d946e03..d94a99e08fa 100644 --- a/CIME/test_scheduler.py +++ b/CIME/test_scheduler.py @@ -28,7 +28,6 @@ get_project, get_timestamp, get_cime_default_driver, - clear_folder, CIMEError, ) from CIME.config import Config @@ -63,6 +62,7 @@ RUN_PHASE, ] # Order matters + ############################################################################### def _translate_test_names_for_new_pecount(test_names, force_procs, force_threads): ############################################################################### @@ -130,6 +130,8 @@ def _translate_test_names_for_new_pecount(test_names, force_procs, force_threads _TIME_CACHE = {} + + ############################################################################### def _get_time_est(test, baseline_root, as_int=False, use_cache=False, raw=False): ############################################################################### @@ -357,10 +359,7 @@ def __init__( if os.path.isdir(test_baseline): existing_baselines.append(test_baseline) if allow_baseline_overwrite and run_count == 0: - if self._namelists_only: - clear_folder(os.path.join(test_baseline, "CaseDocs")) - else: - clear_folder(test_baseline) + pass elif skip_tests_with_existing_baselines: tests_to_skip.append(test_name) expect( @@ -881,7 +880,7 @@ def _xml_phase(self, test): envtest.set_test_parameter("USE_ESMF_LIB", "TRUE") logger.debug(" USE_ESMF_LIB set to TRUE") - elif opt == "CG": + elif opt == "cG": envtest.set_test_parameter("CALENDAR", "GREGORIAN") logger.debug(" CALENDAR set to {}".format(opt)) diff --git a/CIME/test_utils.py b/CIME/test_utils.py index d4daa131496..6dea4e3bddc 100644 --- a/CIME/test_utils.py +++ b/CIME/test_utils.py @@ -26,10 +26,6 @@ def get_tests_from_xml( """ listoftests = [] testlistfiles = [] - if machine is not None: - thismach = machine - if compiler is not None: - thiscompiler = compiler if xml_testlist is not None: expect( @@ -62,8 +58,13 @@ def get_tests_from_xml( for test in newtests: if machine is None: thismach = test["machine"] + else: + thismach = machine if compiler is None: thiscompiler = test["compiler"] + else: + thiscompiler = compiler + test["name"] = CIME.utils.get_full_test_name( test["testname"], grid=test["grid"], diff --git a/CIME/tests/case_fake.py b/CIME/tests/case_fake.py index 2b1117edf63..4510165a5d7 100644 --- a/CIME/tests/case_fake.py +++ b/CIME/tests/case_fake.py @@ -29,6 +29,7 @@ def __init__(self, case_root, create_case_root=True): self.set_value("CASE", casename) self.set_value("CASEBASEID", casename) self.set_value("RUN_TYPE", "startup") + self.set_value("NINST_MAX", 1) self.set_exeroot() self.set_rundir() diff --git a/CIME/tests/test_sys_test_scheduler.py b/CIME/tests/test_sys_test_scheduler.py index 6c90932bb7a..1b72ed78d0b 100755 --- a/CIME/tests/test_sys_test_scheduler.py +++ b/CIME/tests/test_sys_test_scheduler.py @@ -586,6 +586,86 @@ def test_e_test_inferred_compiler(self): ) self.assertEqual(os.path.split(result)[1], "gnuX") + def test_f_baseline_not_cleared_on_failure(self): + """ + Verify that existing baselines are preserved when a regeneration attempt + fails. Before the fix, clear_folder ran in the TestScheduler constructor + (before the test executed), so a failing run left the case with no + baselines. After the fix, clearing is deferred to _generate_baseline_impl + which is only reached when the run phase succeeds. + + Steps: + 1. Generate baselines with TESTRUNFAIL_PASS set (test passes). + 2. Attempt regeneration WITHOUT TESTRUNFAIL_PASS (run fails). + Baselines must remain intact. + 3. Compare with TESTRUNFAIL_PASS set – should succeed because the + original baselines from step 1 are still present. + """ + test_name = "TESTRUNFAIL_P1.f19_g16.A" + if self._config.create_test_flag_mode == "e3sm": + genargs = ["-g", "-o", "-b", self._baseline_name, test_name] + compargs = ["-c", "-b", self._baseline_name, test_name] + else: + genargs = ["-g", self._baseline_name, "-o", test_name] + compargs = ["-c", self._baseline_name, test_name] + + # Step 1: Generate initial baselines with a passing run. + os.environ["TESTRUNFAIL_PASS"] = "True" + try: + self._create_test(genargs) + finally: + del os.environ["TESTRUNFAIL_PASS"] + + baseline_dir = os.path.join(self._baseline_area, self._baseline_name) + self.assertTrue( + os.path.isdir(baseline_dir), "Baseline dir should exist after generation" + ) + baseline_cases = glob.glob(os.path.join(baseline_dir, "*TESTRUNFAIL*")) + self.assertEqual( + len(baseline_cases), 1, "Expected exactly one TESTRUNFAIL baseline dir" + ) + + # Collect the set of regular files created by the baseline generation. + # CaseDocs is a directory managed separately by the namelist phase and + # may be legitimately refreshed during a subsequent SETUP, so we only + # track files here. + baseline_case_dir = baseline_cases[0] + baseline_files_before = { + f + for f in os.listdir(baseline_case_dir) + if os.path.isfile(os.path.join(baseline_case_dir, f)) + } + self.assertGreater( + len(baseline_files_before), + 0, + "Baseline must contain at least one file after generation", + ) + + # Step 2: Attempt regeneration WITHOUT TESTRUNFAIL_PASS – run will fail. + # Our fix ensures that baseline files are NOT cleared before the run + # completes successfully. + self._create_test(genargs, run_errors=True) + + baseline_cases_after = glob.glob(os.path.join(baseline_dir, "*TESTRUNFAIL*")) + self.assertEqual(len(baseline_cases_after), 1) + baseline_files_after = { + f + for f in os.listdir(baseline_cases_after[0]) + if os.path.isfile(os.path.join(baseline_cases_after[0], f)) + } + self.assertEqual( + baseline_files_before, + baseline_files_after, + msg="Baseline files were modified by a failed generation attempt", + ) + + # Step 3: Comparison against the preserved baselines should succeed. + os.environ["TESTRUNFAIL_PASS"] = "True" + try: + self._create_test(compargs) + finally: + del os.environ["TESTRUNFAIL_PASS"] + if __name__ == "__main__": unittest.main() diff --git a/CIME/tests/test_sys_unittest.py b/CIME/tests/test_sys_unittest.py index 3baeacac038..8bfd21eb16f 100755 --- a/CIME/tests/test_sys_unittest.py +++ b/CIME/tests/test_sys_unittest.py @@ -30,20 +30,20 @@ def _has_unit_test_support(self): macros_to_check = [ os.path.join( cmake_macros_dir, - "{}_{}.cmake".format(self._compiler, self._machine), + "{}_{}.cmake".format(self._machine, self._compiler), ), os.path.join(cmake_macros_dir, "{}.cmake".format(self._machine)), os.path.join( os.environ.get("HOME"), ".cime", - "{}_{}.cmake".format(self._compiler, self._machine), + "{}_{}.cmake".format(self._machine, self._compiler), ), os.path.join( os.environ.get("HOME"), ".cime", "{}.cmake".format(self._machine) ), os.path.join( cmake_machine_macros_dir, - "{}_{}.cmake".format(self._compiler, self._machine), + "{}_{}.cmake".format(self._machine, self._compiler), ), os.path.join(cmake_machine_macros_dir, "{}.cmake".format(self._machine)), ] diff --git a/CIME/tests/test_sys_wait_for_tests.py b/CIME/tests/test_sys_wait_for_tests.py index 0377d65771e..3484c9af7a5 100644 --- a/CIME/tests/test_sys_wait_for_tests.py +++ b/CIME/tests/test_sys_wait_for_tests.py @@ -9,6 +9,7 @@ from CIME import utils from CIME import test_status +from CIME.wait_for_tests import ENV_VAR_KEEP_CDASH from CIME.tests import base from CIME.tests import utils as test_utils @@ -110,6 +111,8 @@ def tearDown(self): for testdir in self._testdirs: shutil.rmtree(testdir) + os.environ.pop(ENV_VAR_KEEP_CDASH, None) + def simple_test(self, testdir, expected_results, extra_args="", build_name=None): # Need these flags to test dashboard if e3sm if self._config.create_test_flag_mode == "e3sm" and build_name is not None: @@ -270,6 +273,7 @@ def test_wait_for_test_cdash_pass(self): def test_wait_for_test_cdash_kill(self): expected_results = ["PEND" if item == 5 else "PASS" for item in range(10)] + os.environ[ENV_VAR_KEEP_CDASH] = "True" build_name = "regression_test_kill_" + self._timestamp run_thread = threading.Thread( target=self.threaded_test, diff --git a/CIME/tests/test_unit_bless_test_results.py b/CIME/tests/test_unit_bless_test_results.py index 92aab87b4df..3138dd75c77 100644 --- a/CIME/tests/test_unit_bless_test_results.py +++ b/CIME/tests/test_unit_bless_test_results.py @@ -1,6 +1,7 @@ import re import unittest import tempfile +from contextlib import ExitStack from unittest import mock from pathlib import Path @@ -13,6 +14,8 @@ is_hist_bless_needed, ) from CIME.test_status import ALL_PHASES, GENERATE_PHASE +from CIME.tests import utils as test_utils +from CIME.utils import CIMEError class TestUnitBlessTestResults(unittest.TestCase): @@ -918,6 +921,44 @@ def test_bless_tests_no_match(self, get_test_status_files, TestStatus, Case): assert success + # TODO should update all tests to use mock_case + def test_bless_results_invalid_case(self): + with ExitStack() as stack: + tempdir = stack.enter_context(tempfile.TemporaryDirectory()) + tests = [ + ( + "SMS.T62_oQU120_ais20.A", + "PASS {0} CREATE_NEWCASE\nPASS {0} XML\nPASS {0} SETUP", + ), + ( + "SMS.f19_g16.X", + "PASS {0} CREATE_NEWCASE\nPASS {0} XML\nPASS {0} SETUP", + ), + ] + + # call outer function with None to return mocked objects + cases = [ + test_utils.mock_case( + casename=f"{x[0]}.dane_oneapi-ifx.20251219_120951_epolnk", + tempdir=tempdir, + )(None) + for x in tests + ] + + caseroots = [Path(x[2]) for x in cases] + + # write a simple test status file + for i, x in enumerate(caseroots): + x.mkdir(parents=True, exist_ok=True) + with (x / "TestStatus").open("w") as f: + f.write(tests[i][1].format(tests[i][0])) + + success = bless_test_results( + "master", "/tmp/baseline", str(caseroots[0].parent), "oneapi-ifx" + ) + + self.assertFalse(success) + @mock.patch("CIME.bless_test_results.Case") @mock.patch("CIME.bless_test_results.TestStatus") @mock.patch("CIME.bless_test_results.get_test_status_files") diff --git a/CIME/tests/test_unit_case_run.py b/CIME/tests/test_unit_case_run.py index 8f188925d57..88c746d0d8c 100644 --- a/CIME/tests/test_unit_case_run.py +++ b/CIME/tests/test_unit_case_run.py @@ -10,7 +10,7 @@ def _case_post_run_check(): case = mock.MagicMock() # RUNDIR, COMP_INTERFACE, COMP_CPL, COMP_ATM, COMP_OCN, MULTI_DRIVER - case.get_value.side_effect = ("/tmp/run", "mct", "cpl", "satm", "socn", False) + case.get_value.side_effect = ("/tmp/run", "mct", "cpl", 1, "satm", "socn", False) # COMP_CLASSES case.get_values.return_value = ("CPL", "ATM", "OCN") diff --git a/CIME/tests/test_unit_case_st_archive.py b/CIME/tests/test_unit_case_st_archive.py new file mode 100644 index 00000000000..bf27ab1c201 --- /dev/null +++ b/CIME/tests/test_unit_case_st_archive.py @@ -0,0 +1,348 @@ +#!/usr/bin/env python3 + +import tempfile +import unittest +from pathlib import Path +from unittest import mock + +from CIME import date +from CIME.case import case_st_archive +from CIME.XML import env_archive as _env_archive +from CIME.tests import utils + +UNSET = r""" + + + unset + $CASE.cpl$NINST_STRING.r.$DATENAME.nc + + + +""" + +UNSET_CONTENT = r""" + + + rpointer.cpl$NINST_STRING.$DATENAME + unset + + + +""" + +SINGLE_NINST_DATE = r""" + + + rpointer.cpl$NINST_STRING.$DATENAME + $CASE.cpl$NINST_STRING.r.$DATENAME.nc + + + +""" + +SINGLE_NINST_MPAS_DATE = r""" + + + rpointer.cpl$NINST_STRING.$DATENAME + $CASE.cpl$NINST_STRING.r.$MPAS_DATENAME.nc + + + +""" + + +def write_files(*files): + for file, content in files: + p = Path(file) + p.parent.mkdir(parents=True, exist_ok=True) + p.open("w").write(content or "") + + +class TestArchiveRpointerFiles(unittest.TestCase): + @mock.patch("shutil.move") + @mock.patch("CIME.case.case_st_archive.safe_copy") + @utils.mock_env(_env_archive.EnvArchive, SINGLE_NINST_MPAS_DATE) + def test_generate_rpointer_mpas(self, env_archive, safe_copy, move): + archive_entry = env_archive.get_archive_specs()[0] + + with tempfile.TemporaryDirectory() as tempdir: + run_dir = Path(tempdir, "run") + rest_dir = Path(tempdir, "archive", "rest", "0001-01-01") + rest_dir.mkdir(parents=True) + + case_st_archive._archive_rpointer_files( + "case", + [], + str(run_dir), + True, + env_archive, + archive_entry, + str(rest_dir), + date.date(1, 1, 1), + False, + ) + + safe_copy.assert_not_called() + + move.assert_not_called() + + # should have created the file + generated_files = list(rest_dir.glob("*")) + assert generated_files == [rest_dir / "rpointer.cpl.0001-01-01-00000"] + + with (rest_dir / "rpointer.cpl.0001-01-01-00000").open("r") as f: + content = f.read() + + # check content + assert content == "case.cpl.r.0001-01-01_00:00:00.nc \n" + + @mock.patch("shutil.move") + @mock.patch("CIME.case.case_st_archive.safe_copy") + @utils.mock_env(_env_archive.EnvArchive, SINGLE_NINST_DATE) + def test_generate_rpointer(self, env_archive, safe_copy, move): + archive_entry = env_archive.get_archive_specs()[0] + + with tempfile.TemporaryDirectory() as tempdir: + run_dir = Path(tempdir, "run") + rest_dir = Path(tempdir, "archive", "rest", "0001-01-01") + rest_dir.mkdir(parents=True) + + case_st_archive._archive_rpointer_files( + "case", + [], + str(run_dir), + True, + env_archive, + archive_entry, + str(rest_dir), + date.date(1, 1, 1), + False, + ) + + safe_copy.assert_not_called() + + move.assert_not_called() + + # should have created the file + generated_files = list(rest_dir.glob("*")) + assert generated_files == [rest_dir / "rpointer.cpl.0001-01-01-00000"] + + with (rest_dir / "rpointer.cpl.0001-01-01-00000").open("r") as f: + content = f.read() + + # check content + assert content == "case.cpl.r.0001-01-01-00000.nc \n" + + @mock.patch("shutil.move") + @mock.patch("CIME.case.case_st_archive.safe_copy") + @utils.mock_env(_env_archive.EnvArchive, UNSET_CONTENT) + def test_generate_rpointer_unset_content(self, env_archive, safe_copy, move): + archive_entry = env_archive.get_archive_specs()[0] + + with tempfile.TemporaryDirectory() as tempdir: + run_dir = Path(tempdir, "run") + rest_dir = Path(tempdir, "archive", "rest", "0001-01-01") + rest_dir.mkdir(parents=True) + + case_st_archive._archive_rpointer_files( + "case", + [], + str(run_dir), + True, + env_archive, + archive_entry, + str(rest_dir), + date.date(1, 1, 1), + False, + ) + + safe_copy.assert_not_called() + + move.assert_not_called() + + # should have created the file + generated_files = list(rest_dir.glob("*")) + assert generated_files == [] + + @mock.patch("shutil.move") + @mock.patch("CIME.case.case_st_archive.safe_copy") + @utils.mock_env(_env_archive.EnvArchive, SINGLE_NINST_DATE) + def test_ninst_generate_rpointer(self, env_archive, safe_copy, move): + archive_entry = env_archive.get_archive_specs()[0] + + with tempfile.TemporaryDirectory() as tempdir: + run_dir = Path(tempdir, "run") + rest_dir = Path(tempdir, "archive", "rest", "0001-01-01") + rest_dir.mkdir(parents=True) + + case_st_archive._archive_rpointer_files( + "case", + ["01", "02"], + str(run_dir), + True, + env_archive, + archive_entry, + str(rest_dir), + date.date(1, 1, 1), + False, + ) + + safe_copy.assert_not_called() + + move.assert_not_called() + + # should have created the file + generated_files = list(rest_dir.glob("*")) + assert sorted(generated_files) == sorted( + [ + rest_dir / "rpointer.cpl01.0001-01-01-00000", + rest_dir / "rpointer.cpl02.0001-01-01-00000", + ] + ) + + with (rest_dir / "rpointer.cpl01.0001-01-01-00000").open("r") as f: + content = f.read() + + # check content + assert content == "case.cpl01.r.0001-01-01-00000.nc \n" + + @mock.patch("shutil.move") + @mock.patch("CIME.case.case_st_archive.safe_copy") + @utils.mock_env(_env_archive.EnvArchive, SINGLE_NINST_DATE) + def test_datename_is_last(self, env_archive, safe_copy, move): + archive_entry = env_archive.get_archive_specs()[0] + + with tempfile.TemporaryDirectory() as tempdir: + run_dir = Path(tempdir, "run") + rest_dir = Path(tempdir, "archive", "rest", "0001-01-01") + rest_dir.mkdir(parents=True) + + write_files( + (run_dir / "rpointer.cpl.0001-01-01-00000", ""), + ) + + case_st_archive._archive_rpointer_files( + "case", + [], + str(run_dir), + True, + env_archive, + archive_entry, + str(rest_dir), + date.date(1, 1, 1), + True, + ) + + safe_copy.assert_any_call( + str(run_dir / "rpointer.cpl.0001-01-01-00000"), + str(rest_dir / "rpointer.cpl.0001-01-01-00000"), + ) + + move.assert_not_called() + + @mock.patch("shutil.move") + @mock.patch("CIME.case.case_st_archive.safe_copy") + @utils.mock_env(_env_archive.EnvArchive, SINGLE_NINST_DATE) + def test_ninst(self, env_archive, safe_copy, move): + archive_entry = env_archive.get_archive_specs()[0] + + with tempfile.TemporaryDirectory() as tempdir: + run_dir = Path(tempdir, "run") + rest_dir = Path(tempdir, "archive", "rest", "0001-01-01") + rest_dir.mkdir(parents=True) + + write_files( + (run_dir / "rpointer.cpl.01.0001-01-01-00000", ""), + (run_dir / "rpointer.cpl.02.0001-01-01-00000", ""), + ) + + case_st_archive._archive_rpointer_files( + "case", + ["01", "02"], + str(run_dir), + True, + env_archive, + archive_entry, + str(rest_dir), + date.date(1, 1, 1), + False, + ) + + safe_copy.assert_not_called() + + move.assert_any_call( + str(run_dir / "rpointer.cpl.01.0001-01-01-00000"), + str(rest_dir / "rpointer.cpl.01.0001-01-01-00000"), + ) + move.assert_any_call( + str(run_dir / "rpointer.cpl.02.0001-01-01-00000"), + str(rest_dir / "rpointer.cpl.02.0001-01-01-00000"), + ) + + @mock.patch("shutil.move") + @mock.patch("CIME.case.case_st_archive.safe_copy") + @utils.mock_env(_env_archive.EnvArchive, SINGLE_NINST_DATE) + def test_single_rpointer(self, env_archive, safe_copy, move): + archive_entry = env_archive.get_archive_specs()[0] + + with tempfile.TemporaryDirectory() as tempdir: + run_dir = Path(tempdir, "run") + rest_dir = Path(tempdir, "archive", "rest", "0001-01-01") + rest_dir.mkdir(parents=True) + + write_files( + (run_dir / "rpointer.cpl.0001-01-01-00000", ""), + ) + + case_st_archive._archive_rpointer_files( + "case", + [], + str(run_dir), + True, + env_archive, + archive_entry, + str(rest_dir), + date.date(1, 1, 1), + False, + ) + + safe_copy.assert_not_called() + + move.assert_not_called() + + # should have created the file + generated_files = list(rest_dir.glob("*")) + assert generated_files == [rest_dir / "rpointer.cpl.0001-01-01-00000"] + + with (rest_dir / "rpointer.cpl.0001-01-01-00000").open("r") as f: + content = f.read() + + # check content + assert content == "case.cpl.r.0001-01-01-00000.nc \n" + + @mock.patch("shutil.move") + @mock.patch("CIME.case.case_st_archive.safe_copy") + @utils.mock_env(_env_archive.EnvArchive, UNSET) + def test_unset(self, env_archive, safe_copy, move): + archive_entry = env_archive.get_archive_specs()[0] + + with tempfile.TemporaryDirectory() as tempdir: + run_dir = Path(tempdir, "run") + rest_dir = Path(tempdir, "archive", "rest", "0001-01-01") + rest_dir.mkdir(parents=True) + + case_st_archive._archive_rpointer_files( + "case", + [], + str(run_dir), + True, + env_archive, + archive_entry, + str(rest_dir), + date.date(1, 1, 1), + False, + ) + + safe_copy.assert_not_called() + + move.assert_not_called() diff --git a/CIME/tests/test_unit_xml_tests.py b/CIME/tests/test_unit_xml_tests.py index a79bb3b9c0a..898e00bb0b8 100644 --- a/CIME/tests/test_unit_xml_tests.py +++ b/CIME/tests/test_unit_xml_tests.py @@ -32,16 +32,27 @@ def test_support_single_exe(self, _setup_cases_if_not_yet_done): case.get_compset_components.return_value = () - case.get_value.side_effect = ( - "SMS", - tdir, - f"{caseroot}", - "SMS.f19_g16.S", - "cpl", - "SMS.f19_g16.S", - f"{caseroot}", - "SMS.f19_g16.S", - ) + def fake_get_value(item, attribute=None): + simple_lookup = { + "TESTCASE": "SMS", + "CASEROOT": f"{caseroot}", + "CASEBASEID": "SMS.f19_g16.S", + "COMP_INTERFACE": "cpl", + "DRV_RESTART_POINTER": None, + } + if item in simple_lookup: + return simple_lookup[item] + elif item == "SYSTEM_TESTS_DIR": + if attribute["component"] == "any": + return tdir + else: + return None + + raise KeyError( + f"Unmocked call: case.get_value({item}, attribute={attribute})" + ) + + case.get_value.side_effect = fake_get_value tests = Tests() @@ -65,17 +76,28 @@ def test_support_single_exe_error(self, _setup_cases_if_not_yet_done): case.get_compset_components.return_value = () - case.get_value.side_effect = ( - "ERP", - tdir, - f"{caseroot}", - "ERP.f19_g16.S", - "cpl", - None, - "ERP.f19_g16.S", - f"{caseroot}", - "ERP.f19_g16.S", - ) + def fake_get_value(item, attribute=None): + simple_lookup = { + "TESTCASE": "ERP", + "CASEROOT": f"{caseroot}", + "CASEBASEID": "ERP.f19_g16.S", + "CASE": "ERP.f19_g16.S", + "COMP_INTERFACE": "cpl", + "DRV_RESTART_POINTER": None, + } + if item in simple_lookup: + return simple_lookup[item] + elif item == "SYSTEM_TESTS_DIR": + if attribute["component"] == "any": + return tdir + else: + return None + + raise KeyError( + f"Unmocked call: case.get_value({item}, attribute={attribute})" + ) + + case.get_value.side_effect = fake_get_value tests = Tests() diff --git a/CIME/tests/utils.py b/CIME/tests/utils.py index 951b5a8390e..5987f0fd18e 100644 --- a/CIME/tests/utils.py +++ b/CIME/tests/utils.py @@ -165,43 +165,89 @@ def __str__(self): return ET.tostring(self.root.xml_element, encoding="unicode", method="xml") -def mock_case(*args, empty_env=False, filename=None, mock_set_value=False, **kwargs): +def mock_case( + *args, + empty_env=False, + filename=None, + mock_set_value=False, + casename=None, + tempdir=None, + **kwargs, +): if filename is None: filename = "env_test.xml" - def outer(func): - # patch "read_xml" function since the source file usually doesn't exist - @mock.patch("CIME.case.case.Case.read_xml") - def wrapper(self, read_xml, *args, **kwargs): - with tempfile.TemporaryDirectory() as tempdir: - caseroot = f"{tempdir}/case" - with Case(caseroot, read_only=False) as case: - case.flush = lambda: None - - env = TestEnv() - env.filename = f"{os.getcwd()}/{filename}" - - if not empty_env: - case._files = [env] - - case._env_entryid_files = [env] - - if mock_set_value: - case.set_value = mock.MagicMock() - - func( - self, - *args, - **kwargs, - case_read_xml=read_xml, - test_env=env, - caseroot=caseroot, - case=case, - ) + if casename is None: + casename = "case" + + with contextlib.ExitStack() as stack: + if tempdir is None: + tempdir = stack.enter_context(tempfile.TemporaryDirectory()) + + def outer(func): + # patch "read_xml" function since the source file usually doesn't exist + @mock.patch("CIME.case.case.Case.read_xml") + def wrapper(self, read_xml, *args, **kwargs): + caseroot = f"{tempdir}/{casename}" + + case = stack.enter_context(Case(caseroot, read_only=False)) + case.flush = lambda: None + + env = TestEnv() + env.filename = f"{os.getcwd()}/{filename}" + + if not empty_env: + case._files = [env] + + case._env_entryid_files = [env] + + if mock_set_value: + case.set_value = mock.MagicMock() + + # + if self is None: + return read_xml, env, caseroot, casename, case + + func( + self, + *args, + **kwargs, + case_read_xml=read_xml, + test_env=env, + caseroot=caseroot, + case=case, + ) + + # not wrapping function + if func is None: + return wrapper(None) + + return wrapper + + return outer + + +def mock_env(cls, content=None): + """Mocks an environment class. + + Args: + cls: XML environment class. + content: String with the XML to load. + """ + + def decorator(f): + env = cls(read_only=False) + + if content is not None: + env.read_only = True + env.read_fd(io.StringIO(content)) + + def wrapper(self, *args, **kwargs): + return f(self, env, *args, **kwargs) return wrapper - return outer + return decorator @contextlib.contextmanager diff --git a/CIME/utils.py b/CIME/utils.py index 2b48980b703..e4a49c4457e 100644 --- a/CIME/utils.py +++ b/CIME/utils.py @@ -2267,7 +2267,7 @@ def find_system_test(testname, case): if testname.startswith("TEST"): system_test_path = "CIME.SystemTests.system_tests_common.{}".format(testname) else: - components = ["any"] + components = ["any", "allactive"] components.extend(case.get_compset_components()) fdir = [] for component in components: @@ -2276,6 +2276,11 @@ def find_system_test(testname, case): ) if tdir is not None: tdir = os.path.abspath(tdir) + if tdir in fdir: + # This can happen if multiple SYSTEM_TESTS_DIRs resolve to the same + # path; in this case, we just want to handle the first occurrence and + # skip the rest. + continue system_test_file = os.path.join(tdir, "{}.py".format(testname.lower())) if os.path.isfile(system_test_file): fdir.append(tdir) @@ -2692,6 +2697,7 @@ def is_comp_standalone(case): """ stubcnt = 0 classes = case.get_values("COMP_CLASSES") + model = "cpl" for comp in classes: if case.get_value("COMP_{}".format(comp)) == "s{}".format(comp.lower()): stubcnt = stubcnt + 1 @@ -2700,4 +2706,4 @@ def is_comp_standalone(case): numclasses = len(classes) if stubcnt >= numclasses - 2: return True, model - return False, get_model() + return False, None diff --git a/CIME/wait_for_tests.py b/CIME/wait_for_tests.py index 928b010ba30..1f6940138a9 100644 --- a/CIME/wait_for_tests.py +++ b/CIME/wait_for_tests.py @@ -1,13 +1,13 @@ # pylint: disable=import-error -import queue -import os, time, threading, socket, signal, shutil, glob +import queue, os, time, threading, socket, signal, shutil, glob, tempfile +from pathlib import Path # pylint: disable=import-error import logging import xml.etree.ElementTree as xmlet import CIME.utils -from CIME.utils import expect, Timeout, run_cmd_no_fail, safe_copy, CIMEError +from CIME.utils import expect, Timeout, run_cmd, run_cmd_no_fail, safe_copy, CIMEError from CIME.XML.machines import Machines from CIME.test_status import * from CIME.provenance import save_test_success @@ -17,6 +17,7 @@ E3SM_MAIN_CDASH = "E3SM" CDASH_DEFAULT_BUILD_GROUP = "ACME_Latest" SLEEP_INTERVAL_SEC = 0.1 +ENV_VAR_KEEP_CDASH = "CIME_TEST_CDASH_WFT" ############################################################################### def signal_handler(*_): @@ -92,24 +93,16 @@ def create_cdash_xml_boiler( utc_time, current_time, hostname, - git_commit, ): ############################################################################### site_elem = xmlet.Element("Site") - if "JENKINS_START_TIME" in os.environ: - time_info_str = "Total testing time: {:d} seconds".format( - int(current_time) - int(os.environ["JENKINS_START_TIME"]) - ) - else: - time_info_str = "" - site_elem.attrib["BuildName"] = cdash_build_name site_elem.attrib["BuildStamp"] = "{}-{}".format(utc_time, cdash_build_group) site_elem.attrib["Name"] = hostname site_elem.attrib["OSName"] = "Linux" site_elem.attrib["Hostname"] = hostname - site_elem.attrib["OSVersion"] = "Commit: {}{}".format(git_commit, time_info_str) + site_elem.attrib["OSVersion"] = "Unknown" phase_elem = xmlet.SubElement(site_elem, phase) @@ -130,7 +123,6 @@ def create_cdash_config_xml( current_time, hostname, data_rel_path, - git_commit, ): ############################################################################### site_elem, config_elem = create_cdash_xml_boiler( @@ -140,7 +132,6 @@ def create_cdash_config_xml( utc_time, current_time, hostname, - git_commit, ) xmlet.SubElement(config_elem, "ConfigureCommand").text = "namelists" @@ -165,7 +156,7 @@ def create_cdash_config_xml( xmlet.SubElement(config_elem, "ElapsedMinutes").text = "0" # Skip for now etree = xmlet.ElementTree(site_elem) - etree.write(os.path.join(data_rel_path, "Configure.xml")) + etree.write(data_rel_path / "Configure.xml") ############################################################################### @@ -177,7 +168,6 @@ def create_cdash_build_xml( current_time, hostname, data_rel_path, - git_commit, ): ############################################################################### site_elem, build_elem = create_cdash_xml_boiler( @@ -187,7 +177,6 @@ def create_cdash_build_xml( utc_time, current_time, hostname, - git_commit, ) xmlet.SubElement(build_elem, "ConfigureCommand").text = "case.build" @@ -214,7 +203,7 @@ def create_cdash_build_xml( xmlet.SubElement(build_elem, "ElapsedMinutes").text = "0" # Skip for now etree = xmlet.ElementTree(site_elem) - etree.write(os.path.join(data_rel_path, "Build.xml")) + etree.write(data_rel_path / "Build.xml") ############################################################################### @@ -226,7 +215,6 @@ def create_cdash_test_xml( current_time, hostname, data_rel_path, - git_commit, ): ############################################################################### site_elem, testing_elem = create_cdash_xml_boiler( @@ -236,7 +224,6 @@ def create_cdash_test_xml( utc_time, current_time, hostname, - git_commit, ) test_list_elem = xmlet.SubElement(testing_elem, "TestList") @@ -298,28 +285,20 @@ def create_cdash_test_xml( xmlet.SubElement(testing_elem, "ElapsedMinutes").text = "0" # Skip for now etree = xmlet.ElementTree(site_elem) - - etree.write(os.path.join(data_rel_path, "Test.xml")) + etree.write(data_rel_path / "Test.xml") ############################################################################### def create_cdash_xml_fakes( - results, cdash_build_name, cdash_build_group, utc_time, current_time, hostname + results, + cdash_build_name, + cdash_build_group, + utc_time, + current_time, + hostname, + data_rel_path, ): ############################################################################### - # We assume all cases were created from the same code repo - first_result_case = os.path.dirname(list(results.items())[0][1][0]) - try: - srcroot = run_cmd_no_fail( - "./xmlquery --value SRCROOT", from_dir=first_result_case - ) - except CIMEError: - # Use repo containing this script as last resort - srcroot = os.path.join(CIME.utils.get_cime_root(), "..") - - git_commit = CIME.utils.get_current_commit(repo=srcroot) - - data_rel_path = os.path.join("Testing", utc_time) create_cdash_config_xml( results, @@ -329,7 +308,6 @@ def create_cdash_xml_fakes( current_time, hostname, data_rel_path, - git_commit, ) create_cdash_build_xml( @@ -340,7 +318,6 @@ def create_cdash_xml_fakes( current_time, hostname, data_rel_path, - git_commit, ) create_cdash_test_xml( @@ -351,88 +328,86 @@ def create_cdash_xml_fakes( current_time, hostname, data_rel_path, - git_commit, ) ############################################################################### def create_cdash_upload_xml( - results, cdash_build_name, cdash_build_group, utc_time, hostname, force_log_upload + results, + cdash_build_name, + cdash_build_group, + utc_time, + hostname, + force_log_upload, + tmp_path, + data_rel_path, ): ############################################################################### - data_rel_path = os.path.join("Testing", utc_time) - - try: - log_dir = "{}_logs".format(cdash_build_name) - - need_to_upload = False + log_dirname = f"{cdash_build_name}_logs" + log_path = tmp_path / log_dirname + + need_to_upload = False + + for test_name, test_data in results.items(): + test_path, test_status, _ = test_data + + if test_status != TEST_PASS_STATUS or force_log_upload: + test_case_dir = os.path.dirname(test_path) + + case_dirs = [test_case_dir] + case_base = os.path.basename(test_case_dir) + test_case2_dir = os.path.join(test_case_dir, "case2", case_base) + if os.path.exists(test_case2_dir): + case_dirs.append(test_case2_dir) + + for case_dir in case_dirs: + for param in ["EXEROOT", "RUNDIR", "CASEDIR"]: + if param == "CASEDIR": + log_src_dir = case_dir + else: + # it's possible that tests that failed very badly/early, and fake cases for testing + # will not be able to support xmlquery + try: + log_src_dir = run_cmd_no_fail( + "./xmlquery {} --value".format(param), + from_dir=case_dir, + ) + except CIMEError: + continue + + log_dst_dir = log_path / "{}{}_{}_logs".format( + test_name, + "" if case_dir == test_case_dir else ".case2", + param, + ) + log_dst_dir.mkdir(parents=True) + for log_file in glob.glob(os.path.join(log_src_dir, "*log*")): + if os.path.isdir(log_file): + shutil.copytree( + log_file, log_dst_dir / os.path.basename(log_file) + ) + else: + safe_copy(log_file, str(log_dst_dir)) + for log_file in glob.glob( + os.path.join(log_src_dir, "*.cprnc.out*") + ): + safe_copy(log_file, str(log_dst_dir)) - for test_name, test_data in results.items(): - test_path, test_status, _ = test_data + need_to_upload = True - if test_status != TEST_PASS_STATUS or force_log_upload: - test_case_dir = os.path.dirname(test_path) + if need_to_upload: - case_dirs = [test_case_dir] - case_base = os.path.basename(test_case_dir) - test_case2_dir = os.path.join(test_case_dir, "case2", case_base) - if os.path.exists(test_case2_dir): - case_dirs.append(test_case2_dir) + tarball = "{}.tar.gz".format(log_dirname) - for case_dir in case_dirs: - for param in ["EXEROOT", "RUNDIR", "CASEDIR"]: - if param == "CASEDIR": - log_src_dir = case_dir - else: - # it's possible that tests that failed very badly/early, and fake cases for testing - # will not be able to support xmlquery - try: - log_src_dir = run_cmd_no_fail( - "./xmlquery {} --value".format(param), - from_dir=case_dir, - ) - except: - continue - - log_dst_dir = os.path.join( - log_dir, - "{}{}_{}_logs".format( - test_name, - "" if case_dir == test_case_dir else ".case2", - param, - ), - ) - os.makedirs(log_dst_dir) - for log_file in glob.glob(os.path.join(log_src_dir, "*log*")): - if os.path.isdir(log_file): - shutil.copytree( - log_file, - os.path.join( - log_dst_dir, os.path.basename(log_file) - ), - ) - else: - safe_copy(log_file, log_dst_dir) - for log_file in glob.glob( - os.path.join(log_src_dir, "*.cprnc.out*") - ): - safe_copy(log_file, log_dst_dir) - - need_to_upload = True - - if need_to_upload: - - tarball = "{}.tar.gz".format(log_dir) - if os.path.exists(tarball): - os.remove(tarball) - - run_cmd_no_fail( - "tar -cf - {} | gzip -c".format(log_dir), arg_stdout=tarball - ) - base64 = run_cmd_no_fail("base64 {}".format(tarball)) + run_cmd_no_fail( + "tar -cf - {} | gzip -c".format(log_dirname), + arg_stdout=tarball, + from_dir=str(tmp_path), + ) + base64 = run_cmd_no_fail("base64 {}".format(tarball), from_dir=str(tmp_path)) - xml_text = r""" + xml_text = r""" "?> @@ -444,25 +419,26 @@ def create_cdash_upload_xml( """.format( - cdash_build_name, - utc_time, - cdash_build_group, - hostname, - os.path.abspath(tarball), - base64, - ) - - with open(os.path.join(data_rel_path, "Upload.xml"), "w") as fd: - fd.write(xml_text) + cdash_build_name, + utc_time, + cdash_build_group, + hostname, + str((tmp_path / tarball).absolute()), + base64, + ) - finally: - if os.path.isdir(log_dir): - shutil.rmtree(log_dir) + with (data_rel_path / "Upload.xml").open(mode="w") as fd: + fd.write(xml_text) ############################################################################### def create_cdash_xml( - results, cdash_build_name, cdash_project, cdash_build_group, force_log_upload=False + results, + cdash_build_name, + cdash_project, + cdash_build_group, + force_log_upload=False, + cdash_tmproot=None, ): ############################################################################### @@ -482,8 +458,78 @@ def create_cdash_xml( "Could not convert hostname '{}' into an E3SM machine name".format(hostname) ) - for drop_method in ["https", "http"]: - dart_config = """ + # We assume all cases were created from the same code repo + first_result_case = os.path.dirname(list(results.items())[0][1][0]) + try: + srcroot = run_cmd_no_fail( + "./xmlquery --value SRCROOT", from_dir=first_result_case + ) + except CIMEError: + # Use repo containing this script as last resort + srcroot = os.path.join(CIME.utils.get_cime_root(), "..") + + git_commit = CIME.utils.get_current_commit(repo=srcroot) + + # Get total elapsed time + if "JENKINS_START_TIME" in os.environ: + time_info = int(current_time) - int(os.environ["JENKINS_START_TIME"]) + else: + time_info = "unknown" + + if cdash_tmproot: + tmproots = [cdash_tmproot] + else: + tmproots = [None, first_result_case, os.getcwd()] + + # Try multiple tmproots if necessary. The default /tmp will be tried first + # unless cdash_tmproot was provided. The location of the default can be + # modified via the TMPDIR environment variable. + for tmproot in tmproots: + try: + with tempfile.TemporaryDirectory(dir=tmproot) as tmpdir: + tmp_path = Path(tmpdir) + utc_time = time.strftime("%Y%m%d-%H%M", utc_time_tuple) + dart_path = tmp_path / "DartConfiguration.tcl" + testing_path = tmp_path / "Testing" + testtime_dir = testing_path / utc_time # Most action happens here + tag_file = testing_path / "TAG" + notes_file = tmp_path / "notes.txt" + + testtime_dir.mkdir(parents=True) + + # Make tag file + with tag_file.open(mode="w") as tag_fd: + tag_fd.write(f"{utc_time}\n{cdash_build_group}\n") + + # Make notes file + with notes_file.open(mode="w") as notes_fd: + notes_fd.write( + f"Commit {git_commit}\nTotal testing time {time_info} seconds\n" + ) + + create_cdash_xml_fakes( + results, + cdash_build_name, + cdash_build_group, + utc_time, + current_time, + hostname, + testtime_dir, + ) + + create_cdash_upload_xml( + results, + cdash_build_name, + cdash_build_group, + utc_time, + hostname, + force_log_upload, + tmp_path, + testtime_dir, + ) + + for drop_method in ["https", "http"]: + dart_config = """ SourceDirectory: {0} BuildDirectory: {0} @@ -512,55 +558,42 @@ def create_cdash_xml( UseLaunchers: CurlOptions: CURLOPT_SSL_VERIFYPEER_OFF;CURLOPT_SSL_VERIFYHOST_OFF """.format( - os.getcwd(), - hostname, - cdash_build_name, - cdash_project, - shutil.which("scp"), - cdash_timestamp, - drop_method, - ) - - with open("DartConfiguration.tcl", "w") as dart_fd: - dart_fd.write(dart_config) - - utc_time = time.strftime("%Y%m%d-%H%M", utc_time_tuple) - testing_dir = os.path.join("Testing", utc_time) - if os.path.isdir(testing_dir): - shutil.rmtree(testing_dir) - - os.makedirs(os.path.join("Testing", utc_time)) - - # Make tag file - with open("Testing/TAG", "w") as tag_fd: - tag_fd.write("{}\n{}\n".format(utc_time, cdash_build_group)) + str(tmp_path.absolute()), + hostname, + cdash_build_name, + cdash_project, + shutil.which("scp"), + cdash_timestamp, + drop_method, + ) + with dart_path.open(mode="w") as dart_fd: + dart_fd.write(dart_config) - create_cdash_xml_fakes( - results, - cdash_build_name, - cdash_build_group, - utc_time, - current_time, - hostname, - ) + stat, out, _ = run_cmd( + "ctest -VV -D NightlySubmit -A notes.txt", + combine_output=True, + from_dir=str(tmp_path), + ) + if stat != 0: + logging.warning( + "ctest upload drop method {} FAILED:\n{}".format( + drop_method, out + ) + ) + else: + logging.info("Upload SUCCESS:\n{}".format(out)) + if ENV_VAR_KEEP_CDASH in os.environ: + logging.info( + f"Test mode enabled, copying {str(tmp_path)} to {os.getcwd()}" + ) + safe_copy(str(tmp_path / "Testing"), os.getcwd()) - create_cdash_upload_xml( - results, - cdash_build_name, - cdash_build_group, - utc_time, - hostname, - force_log_upload, - ) + return - stat, out, _ = run_cmd("ctest -VV -D NightlySubmit", combine_output=True) - if stat != 0: + except Exception as e: logging.warning( - "ctest upload drop method {} FAILED:\n{}".format(drop_method, out) + f"Using temp root '{tmproot}', cdash submission failed with error {e}" ) - else: - logging.info("Upload SUCCESS:\n{}".format(out)) - return expect(False, "All cdash upload attempts failed") @@ -727,6 +760,7 @@ def wait_for_tests( ignore_memleak=False, cdash_build_name=None, cdash_project=E3SM_MAIN_CDASH, + cdash_tmproot=None, cdash_build_group=CDASH_DEFAULT_BUILD_GROUP, timeout=None, force_log_upload=False, @@ -833,6 +867,7 @@ def wait_for_tests( cdash_project, cdash_build_group, force_log_upload, + cdash_tmproot, ) return all_pass diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a70f247e9d4..88207281515 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -20,11 +20,13 @@ For more information on contributing to open source projects, is a great starting point. Also, checkout the [Zen of Scientific Software Maintenance](https://jrleeman.github.io/ScientificSoftwareMaintenance/) for some guiding principles on how to create high quality scientific software contributions. +The canonical, detailed contributing guide for this repository is included in the source tree at `doc/source/contributing-guide.rst`; please consult that file as the single source of truth for developer workflows, testing, and container usage. + ## Getting Started Interested in helping extend CIME? Have code from your research that you believe others will -find useful? Have a few minutes to tackle an issue? In this guide we will get you setup and -integrated into contributing to CIME! +find useful? Have a few minutes to tackle an issue? This guide will get you set up to contribute +to CIME. ## What Can I Do? * Tackle any unassigned [issues](https://github.com/ESMCI/CIME/issues) you wish! @@ -44,7 +46,7 @@ The goal is to maintain a diverse community that's pleasant for everyone. include multiple bug fixes in a single pull request, but they should be related. For unrelated changes, please submit multiple pull requests. * Do not commit changes to files that are irrelevant to your feature or bugfix - (eg: .gitignore). + (e.g., .gitignore). * Be willing to accept constructive criticism as part of issuing a pull request, since the CIME developers are dedicated to ensuring that new features extend the system robustly and do not introduce new bugs. @@ -53,7 +55,7 @@ The goal is to maintain a diverse community that's pleasant for everyone. ## Reporting a bug When creating a new issue, please be as specific as possible. Include the version -of the code you were using, as well as what operating system you are running. +of the code you were using, as well as what operating system you are running. Include the commit SHA or tag (e.g., output of `git rev-parse HEAD`) and the branch name. If possible, include complete, minimal example code that reproduces the problem. ## Pull Requests @@ -62,34 +64,18 @@ We love pull requests from everyone. Fork, then clone the repo: git clone git@github.com:your-username/CIME.git -Additionally you may need to checkout the submodules with: +You will need to initialize and update submodules: cd CIME - git submodule update --init - -You will need to install CIME dependencies and edit config files -to tell CIME about your development machine. See the [CIME users guide](https://esmci.github.io/cime/users_guide/porting-cime.html) - -Run the scripts_regression_tests: - - cd CIME/tests - python scripts_regression_tests.py - -Alternatively with `pytest`: - - pytest CIME/tests - -Make your change. Add tests for your change. Make the tests pass to the same level as before your changes. + git submodule update --init --recursive - cd CIME/tests - python scripts_regression_tests.py +From here you can edit the code and run the unit tests following this [guide](https://esmci.github.io/cime/versions/master/html/contributing-guide.html#pytest). -Run [pre-commit](https://pre-commit.com/#usage) before committing changes and submitting a PR. +When running the ``unit`` tests you can specify any valid machine e.g. docker and the tests will run. - pip install pre-commit - pre-commit run -a +If you need to run the ``system`` tests you will need to have your respective model checked out and on a supported machine. Alternatively you can use CIME [container](https://esmci.github.io/cime/versions/master/html/contributing-guide.html#docker-container) which is used in our GitHub CI testing. -Commit the changes you made. Chris Beams has written a [guide](https://chris.beams.io/posts/git-commit/) on how to write good commit messages. +Before creating your PR you will need to run the code quality checkers; see this [guide](https://esmci.github.io/cime/versions/master/html/contributing-guide.html#code-quality). Push to your fork and [submit a pull request][pr]. @@ -102,8 +88,9 @@ We may suggest some changes or improvements or alternatives. Some things that will increase the chance that your pull request is accepted: * Write tests. -* Follow [PEP8][pep8] for style. (The `flake8` utility can help with this.) -* Write a [good commit message][commit]. +* Write documentation. +* Follow the [Code Quality guide](https://esmci.github.io/cime/versions/master/html/contributing-guide.html#code-quality) +* Write a good commit message, we recommend using [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) Pull requests will automatically have tests run by a Github Action. This includes running both the unit tests as well as `pre-commit`, which checks diff --git a/doc/requirements.txt b/doc/requirements.txt index bedeb8ab75d..5415ccaf0b3 100644 --- a/doc/requirements.txt +++ b/doc/requirements.txt @@ -1,8 +1,8 @@ -sphinx +sphinx<8.0 sphinxcontrib-programoutput sphinx-rtd-theme sphinx-copybutton evv4esm xmlschema sphinx-toolbox -sphinx-autobuild \ No newline at end of file +sphinx-autobuild diff --git a/doc/source/ccs/model-configuration/variables/pes.rst b/doc/source/ccs/model-configuration/variables/pes.rst index 4d158c89571..e8be806b939 100644 --- a/doc/source/ccs/model-configuration/variables/pes.rst +++ b/doc/source/ccs/model-configuration/variables/pes.rst @@ -326,6 +326,8 @@ following meanings: - The global MPI task of the component root task; if negative, indicates nodes rather than tasks. The root processor for each component is set relative to the MPI global communicator. * - PSTRID - The stride of MPI tasks across the global set of pes (for now set to 1). This variable is currently not used and is a placeholder for future development. + * - EXCL_STRIDE + - Stride of MPI tasks owned exclusively by a component. If 0, exclusivity is disabled. * - NINST - The number of component instances, which are spread evenly across NTASKS. * - COST_PER_NODE diff --git a/doc/source/contributing-guide.rst b/doc/source/contributing-guide.rst index c479ed864ca..e43b1785f92 100644 --- a/doc/source/contributing-guide.rst +++ b/doc/source/contributing-guide.rst @@ -21,9 +21,9 @@ The `Case` class includes an array of the Case env classes. In the `configure` f Testing ------- -CIME splits it's testing into two categories `unit` and `sys`. +CIME splits its tests into two categories: `unit` and `sys`. -The `unit` category covers both doctests and unit tests. While the `sys` category covers regression tests. The tests are named accordingly e.g. `unit` tests are found as `CIME/tests/test_unit*`. +The `unit` category covers doctests and unit tests, while the `sys` category covers regression tests. Tests are named accordingly (e.g., unit tests: `CIME/tests/test_unit*`). How to run the tests ``````````````````````` @@ -41,13 +41,12 @@ To get started install `pytest` and `pytest-cov`. .. code-block:: bash + pip install -r test-requirements.txt pip install pytest pytest-cov - # or - # conda install -c conda-forge pytest pytest-cov Examples ........ -Runing all the ``sys`` and ``unit`` tests. +Running all the ``sys`` and ``unit`` tests. .. code-block:: bash @@ -59,13 +58,13 @@ Running only ``sys`` tests, ``sys`` can be replaced with ``unit`` to run only un pytest CIME/tests/test_sys.* -Runnig a specific test case. +Running a specific test case. .. code-block:: bash pytest CIME/tests/test_unit_case.py -A specific test can be ran with the followin. +A specific test can be run with the following. .. code-block:: bash @@ -80,7 +79,7 @@ You can pass either the module name or the file path of a test. Examples ........ -Runing all the ``sys`` and ``unit`` tests. +Running all the ``sys`` and ``unit`` tests. .. code-block:: bash @@ -98,7 +97,7 @@ Runnig a specific test case. python CIME/tests/scripts_regression_tests.py CIME.tests.test_unit_case -A specific test can be ran with the followin. +A specific test can be run with the following. .. code-block:: bash @@ -117,9 +116,7 @@ Installing pre-commit .. code-block:: bash - pip install pre_commit - # or - # conda install -c conda-forge pre_commit + pip install pre-commit Running pre-commit `````````````````` @@ -138,127 +135,52 @@ If you install these scripts then `pre-commit` will automatically run on `git co Docker container ---------------- -GitHub actions runs all CIME's tests in containers. The dockerfile can be found under the `docker/` directory. - -You can skip building the container and use the same container from the GitHub actions using the following commands. This will pull the latest [image](https://hub.docker.com/r/jasonb87/cime/tags), see the available [run modifiers](#running-the-container) to customize the container. - -The current container supports the ``GNU`` compiler and ``OpenMPI`` library. - -Running -``````` -The default environment is similar to the one used by GitHub Actions. It will clone CIME into `/src/cime`, set `CIME_MODEL=cesm` and run CESM's `checkout_externals`. This will create a minimum base environment to run both unit and system tests. - -The `CIME_MODEL` environment vairable will change the environment that is created. +CIME provides a container that the CI uses to run all the testing. This container -Setting it to `E3SM` will clone E3SM into `/src/E3SM`, checkout the submodules and update the CIME repository using `CIME_REPO` and `CIME_BRANCH`. +can also be used to test locally providing a reproducible environment. The -Setting it to `CESM` will clone CESM into `/src/CESM`, run `checkout_externals` and update the CIME repository using `CIME_REPO` and `CIME_BRANCH`. +compiler is ``GNU`` and the MPI implementation is ``OpenMPI``. -The container can further be modified using the environment variables defined below. +The image can be pulled from ``ghcr.io``. .. code-block:: bash - docker run -it --name cime --hostname docker cime:latest bash - - -.. code-block:: bash - - docker run -it --name cime --hostname docker -e CIME_MODEL=e3sm cime:latest bash - -.. note:: - - It's recommended when running the container to pass `--hostname docker` as it will match the custom machine defined in `config_machines.xml`. If this is omitted, `--machine docker` must be passed to CIME commands in order to use the correct machine definition. - -Environment variables -::::::::::::::::::::: + docker pull ghcr.io/esmci/cime:latest -Environment variables to modify the container environment. - -| Name | Description | Default | -| ---- | ----------- | ------- | -| INIT | Set to false to skip init | true | -| GIT_SHALLOW | Performs shallow checkouts, to save time | false | -| UPDATE_CIME | Setting this will cause the CIME repository to be updated using `CIME_REPO` and `CIME_BRANCH` | "false" | -| CIME_MODEL | Setting this will change which environment is loaded | | -| CIME_REPO | CIME repository URL | https://github.com/ESMCI/cime | -| CIME_BRANCH | CIME branch that will be cloned | master | -| E3SM_REPO | E3SM repository URL | https://github.com/E3SM-Project/E3SM | -| E3SM_BRANCH | E3SM branch that will be cloned | master | -| CESM_REPO | CESM repository URL | https://github.com/ESCOMP/CESM | -| CESM_BRANCH | CESM branch that will be cloned | master | - -Examples -:::::::: -.. code-block:: bash - - docker run -it -e INIT=false cime:latest bash +or can be built locally. The build context needs to be set to the root of the CIME repository. .. code-block:: bash - - docker run -it -e CIME_REPO=https://github.com/user/cime -e CIME_BRANCH=updates_xyz cime:latest bash -Persisting data -::::::::::::::: + docker build -t ghcr.io/esmci/cime:latest -f docker/Dockerfile . -The `config_machines.xml` definition as been setup to provided persistance for inputdata, cases, archives and tools. The following paths can be mounted as volumes to provide persistance. - -* /storage/inputdata -* /storage/cases -* /storage/archives -* /storage/tools +Running +``````` +The container does not provide any source, as such you will need to bind +mount the model+cime directory and define which model is being used. The +following example assumes the model is checked out in ``$SRC_PATH``. .. code-block:: bash - docker run -it -v ${PWD}/data-cache:/storage/inputdata cime:latest bash + docker run -it --rm --hostname docker -e CIME_MODEL=e3sm -v ${SRC_PATH}:/root/model -v ./storage:/root/storage -w /root/E3SM/cime ghcr.io/esmci/cime:latest bash -It's also possible to persist the source git repositories. +This example will drop into a shell where CIME commands or tests can be run. +The options are broken down below. -.. code-block:: bash - - docker run -it -v ${PWD}/src:/src cime:latest bash +- ``--hostname docker`` is required to tell CIME which machine definition to use. +- ``-e CIME_MODEL=e3sm`` defines the model. +- ``-v ${SRC_PATH}:/root/E3SM`` passes through the model source. +- ``-v ./storage:/root/storage`` persist all data; cases, baselines, archive, inputdata. the bind mounts can be broken out if you only want to persist certain input/outputs. +- ``-w /root/E3SM/cime`` set the current working directory to CIME's root. +- ``ghcr.io/esmci/cime:latest`` container image. +- ``bash`` the command to run in the container. -Local git respositories can be mounted as well. +You can even run CIME or testing without a shell. .. code-block:: bash - docker run -v ${PWD}:/src/cime cime:latest bash - - docker run -v ${PWD}:/src/E3SM cime:latest bash - -Building -```````` -The container provides 3 targets. - -* base - Base image with no batch system. -* slurm - Slurm batch system with configuration and single queue. -* pbs - PBS batch system with configuration and single queue. + docker run -it --rm --hostname docker -e CIME_MODEL=e3sm -v ${SRC_PATH}:/root/model -v ./storage:/root/storage -w /root/E3SM/cime ghcr.io/esmci/cime:latest pytest CIME/tests/test_unit* .. code-block:: bash - - docker build -t ghcr.io/ESMCI/cime:latest --target docker/ -Customizing -::::::::::: -When building the container some features can be customized. Multiple `--build-arg` arguments can be passed. + docker run -it --rm --hostname docker -e CIME_MODEL=e3sm -v ${SRC_PATH}:/root/model -v ./storage:/root/storage -w /root/E3SM/cime ghcr.io/esmci/cime:latest ./scripts/create_test SMS.f19_g16.S -.. code-block:: bash - - docker build -t ghcr.io/ESMCI/cime:latest --build-arg {name}={value} docker/ - -+------------------------+-----------------------------------------------+---------+ -| Argument | Description | Default | -+========================+===============================================+=========+ -| MAMBAFORGE_VERSION | Version of the condaforge/mambaforge image | 4.11.0-0| -| | used as a base | | -+------------------------+-----------------------------------------------+---------+ -| PNETCDF_VERSION | Parallel NetCDF version to build | 1.12.1 | -+------------------------+-----------------------------------------------+---------+ -| LIBNETCDF_VERSION | Version of libnetcdf, the default will | 4.8.1 | -| | install the latest | | -+------------------------+-----------------------------------------------+---------+ -| NETCDF_FORTRAN_VERSION | Version of netcdf-fortran, the default will | 4.5.4 | -| | install the latest | | -+------------------------+-----------------------------------------------+---------+ -| ESMF_VERSION | Version of ESMF, the default will install the | 8.2.0 | -| | latest | | -+------------------------+-----------------------------------------------+---------+ diff --git a/doc/source/system_testing.rst b/doc/source/system_testing.rst index 9b539fb9605..94804b2da47 100644 --- a/doc/source/system_testing.rst +++ b/doc/source/system_testing.rst @@ -239,14 +239,14 @@ TESTTYPE Description and writing restarts at day 10. ref2case is a clone of the main case. Short term archiving is on. - case + case (Suffix branch) Do a branch run, starting from restarts written in ref2case, for 9 days and writing restarts at day 5. Short term archiving is off. - case (Suffix base) + case - restart (Suffix rest) Do a restart run from the branch run restarts for 4 days. - Compare component history files '.base' and '.hybrid' at day 19. Short term archiving is off. + Comparisons are done between branch vs. hybrid and rest vs. branch ERP PES counts hybrid (OPENMP/MPI) restart bit-for-bit test from startup, (default 6 days + 5 days). Initial PES set up out of the box @@ -314,7 +314,7 @@ MODIFIERS Description ============ ===================================================================================== _C# Set number of instances to # and use the multi driver (can't use with _N). - _CG CALENDAR set to "GREGORIAN". + _cG CALENDAR set to "GREGORIAN". _D XML variable DEBUG set to "TRUE". diff --git a/docker/.cime/config_machines.v2.xml b/docker/.cime/config_machines.v2.xml index 73b7ea56fb2..e93b632c6c7 100644 --- a/docker/.cime/config_machines.v2.xml +++ b/docker/.cime/config_machines.v2.xml @@ -9,14 +9,14 @@ gnu,gnuX openmpi CIME - /home/cime/timings + /root/storage/timings CIME - /home/cime/cases - /home/cime/inputdata - /home/cime/inputdata-clmforc - /home/cime/archive/$CASE - /home/cime/baselines/$COMPILER - /home/cime/tools/cprnc + /root/storage/cases + /root/storage/inputdata + /root/storage/inputdata-clmforc + /root/storage/archive/$CASE + /root/storage/baselines/$COMPILER + /root/storage/tools/cprnc make 4 e3sm_developer @@ -37,11 +37,12 @@ 1 1 - /opt/conda/envs/e3sm - FALSE - /opt/conda/envs/e3sm - /opt/conda/envs/e3sm - /opt/conda/envs/e3sm + /opt/spack-envs/view + /opt/spack-envs/view + /opt/spack-envs/view + /opt/spack-envs/view/bin:$ENV{PATH} + /opt/spack-envs/view/lib + /opt/spack-envs/view/lib/pkgconfig diff --git a/docker/.cime/docker.cmake b/docker/.cime/docker.cmake index f8439e5e9a2..9251b999c2c 100644 --- a/docker/.cime/docker.cmake +++ b/docker/.cime/docker.cmake @@ -3,18 +3,14 @@ if (COMP_NAME STREQUAL gptl) string(APPEND CPPDEFS " -DHAVE_NANOTIME -DBIT64 -DHAVE_SLASHPROC -DHAVE_GETTIMEOFDAY") endif() -string(APPEND CMAKE_C_FLAGS_RELEASE " -O2 -g") -string(APPEND CMAKE_Fortran_FLAGS_RELEASE " -O2 -g") -string(APPEND CMAKE_CXX_FLAGS_RELEASE " -O2 -g") - -string(APPEND CMAKE_C_FLAGS_DEBUG " -O0") -string(APPEND CMAKE_Fortran_FLAGS_DEBUG " -O0") -string(APPEND CMAKE_CXX_FLAGS_DEBUG " -O0") +string(APPEND CMAKE_C_FLAGS " -I/opt/spack-envs/view/include -O1 -g -fno-fast-math -frounding-math -fsignaling-nans -fno-inline -fno-aggressive-loop-optimizations") +string(APPEND CMAKE_Fortran_FLAGS " -I/opt/spack-envs/view/include -O1 -g -fno-fast-math -frounding-math -fsignaling-nans -fno-inline -fno-aggressive-loop-optimizations -ffpe-trap=invalid,zero,overflow") +string(APPEND CMAKE_CXX_FLAGS " -I/opt/spack-envs/view/include") # required for grid generation tests that use make if (CMAKE_SOURCE_DIR MATCHES "^.*TestGridGeneration.*$") - string(APPEND FFLAGS " -I/opt/conda/envs/$ENV{CIME_MODEL}/include") - string(APPEND SLIBS " -L/opt/conda/envs/$ENV{CIME_MODEL} -lnetcdf -lnetcdff") + string(APPEND FFLAGS " -I/opt/spack-envs/view/include") + string(APPEND SLIBS " -L/opt/spack-envs/view/lib -lnetcdf -lnetcdff") endif() # DEBUGGING variables diff --git a/docker/.cime/docker/config_machines.xml b/docker/.cime/docker/config_machines.xml index 79fb6f4898d..c0ea01f4b05 100644 --- a/docker/.cime/docker/config_machines.xml +++ b/docker/.cime/docker/config_machines.xml @@ -8,14 +8,14 @@ gnu,gnuX openmpi CIME - /home/cime/timings + /root/storage/timings CIME - /home/cime/cases - /home/cime/inputdata - /home/cime/inputdata-clmforc - /home/cime/archive/$CASE - /home/cime/baselines/$COMPILER - /home/cime/tools/cprnc + /root/storage/cases + /root/storage/inputdata + /root/storage/inputdata-clmforc + /root/storage/archive/$CASE + /root/storage/baselines/$COMPILER + /root/storage/tools/cprnc make 4 e3sm_developer @@ -36,11 +36,12 @@ 1 1 - /opt/conda/envs/cesm - FALSE - /opt/conda/envs/cesm - /opt/conda/envs/cesm - /opt/conda/envs/cesm + /opt/spack-envs/view + /opt/spack-envs/view + /opt/spack-envs/view + /opt/spack-envs/view/bin:$ENV{PATH} + /opt/spack-envs/view/lib + /opt/spack-envs/view/lib/pkgconfig diff --git a/docker/Dockerfile b/docker/Dockerfile index b9362775bcf..de90ef73365 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,96 +1,67 @@ -ARG BASE_TAG=latest -FROM condaforge/miniforge3:${BASE_TAG} AS base - -WORKDIR /home/cime - -RUN mkdir -p \ - /home/cime/inputdata/cpl/gridmaps/oQU240 \ - /home/cime/inputdata/share/domains \ - /home/cime/timings \ - /home/cime/cases \ - /home/cime/archive \ - /home/cime/baselines \ - /home/cime/tools \ - && wget -O /home/cime/inputdata/cpl/gridmaps/oQU240/map_oQU240_to_ne4np4_aave.160614.nc \ - https://portal.nersc.gov/project/e3sm/inputdata/cpl/gridmaps/oQU240/map_oQU240_to_ne4np4_aave.160614.nc \ - && wget -O /home/cime/inputdata/share/domains/domain.ocn.ne4np4_oQU240.160614.nc \ - https://portal.nersc.gov/project/e3sm/inputdata/share/domains/domain.ocn.ne4np4_oQU240.160614.nc \ - && wget -O /home/cime/inputdata/share/domains/domain.lnd.ne4np4_oQU240.160614.nc \ - https://portal.nersc.gov/project/e3sm/inputdata/share/domains/domain.lnd.ne4np4_oQU240.160614.nc - -COPY cime.yaml cime.yaml - -RUN apt-get update && \ - DEBIAN_FRONTEND=noninteractive \ - apt-get install -y --no-install-recommends \ - perl libxml-libxml-perl && \ - rm -rf /var/lib/apt/lists/* - -RUN mamba install -y gosu tree - -RUN mamba create -n e3sm -y --file cime.yaml python=3.10 'libnetcdf=4.9.1=*openmpi*' \ - && conda remove -n e3sm --force perl \ - && conda clean -afy \ - && rm -rf /opt/conda/pkgs/* - -RUN mamba create -n cesm -y --file cime.yaml python=3.10 'libnetcdf>=4.9.2=*openmpi*' \ - && conda remove -n cesm --force perl \ - && conda clean -afy \ - && rm -rf /opt/conda/pkgs/* - -COPY .cime .cime -COPY entrypoint.sh /entrypoint.sh - -ENTRYPOINT [ "/entrypoint.sh" ] - -FROM base AS slurm - -RUN apt-get update && \ - DEBIAN_FRONTEND=noninteractive \ - apt-get install -y --no-install-recommends \ - munge slurmd slurm-client slurmctld && \ - rm -rf /var/lib/apt/lists/* && \ - sed -i"" "s/\(.*\)[^<]*\(<\/BATCH_SYSTEM>\)/\1slurm\2/g" ~/.cime/config_machines.xml - -COPY slurm/slurm.conf /etc/slurm-llnl/ -COPY slurm/config_batch.xml /root/.cime/ -COPY slurm/entrypoint_batch.sh /entrypoint_batch.sh - -FROM base AS pbs - -RUN apt-get update && \ - DEBIAN_FRONTEND=noninteractive \ - apt-get install -y --no-install-recommends \ - curl ca-certificates software-properties-common \ - gcc make libtool libhwloc-dev libx11-dev libxt-dev libedit-dev \ - libical-dev ncurses-dev python-dev tcl-dev tk-dev swig libexpat-dev libssl-dev \ - libxext-dev libxft-dev autoconf automake \ - postgresql-12 postgresql-server-dev-all postgresql-contrib \ - expat libedit2 python3 sendmail-bin sudo tcl tk && \ - add-apt-repository ppa:deadsnakes/ppa && \ - apt-get update && \ - DEBIAN_FRONTEND=noninteractive \ - apt-get install -y python3.7 python3.7-dev && \ - rm -rf /var/lib/apt/lists/* - -RUN mkdir /src && pushd /src && \ - curl -LO https://github.com/openpbs/openpbs/archive/refs/tags/v20.0.1.tar.gz && \ - tar -xvf v20.0.1.tar.gz && \ - cd openpbs-20.0.1 && \ - sed -i"" 's/\(#include "list_link.h"\)/\1\n#include /' /src/openpbs-20.0.1/src/lib/Libifl/list_link.c && \ - export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin && \ - ./autogen.sh && \ - PYTHON=/usr/bin/python3.7 \ - CFLAGS="`/usr/bin/python3.7m-config --cflags`" \ - LDFLAGS="`/usr/bin/python3.7m-config --ldflags`" \ - LIBS="-lpthread -lm -lpython3.7m" \ - ./configure --prefix=/opt/pbs && \ - make -j8 && \ - make install && \ - popd && \ - rm -rf /src && \ - sed -i"" "s/\(.*\)[^<]*\(<\/BATCH_SYSTEM>\)/\1pbs\2/g" ~/.cime/config_machines.xml - -COPY pbs/pbs.conf /etc/ -COPY pbs/config_batch.xml /root/.cime/ -COPY pbs/entrypoint_batch.sh /entrypoint_batch.sh +FROM ghcr.io/spack/ubuntu-noble:1.0.4 AS spack + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + build-essential \ + cmake \ + libopenblas-openmp-dev \ + libopenblas0-openmp \ + libxml-libxml-perl \ + libxml2-utils \ + openmpi-bin \ + libopenmpi-dev \ + perl \ + pkg-config \ + python3-dev \ + subversion \ + wget \ + && rm -rf /var/lib/apt/lists/* \ + && mkdir -p /opt/spack-envs + +COPY docker/spack.yaml /opt/spack-envs/spack.yaml + +RUN spack compiler find \ + && spack external find --not-buildable \ + && spack external find --not-buildable openmpi \ + && spack -e /opt/spack-envs install --fail-fast + +ENV PATH=/opt/spack-envs/view/bin:$PATH +ENV PKG_CONFIG_PATH=/opt/spack-envs/view/lib/pkgconfig +ENV LD_LIBRARY_PATH=/opt/spack-envs/view/lib +ENV ESMFMKFILE=/opt/spack-envs/view/lib/esmf.mk + +FROM spack AS cprnc + +COPY CIME/non_py/cprnc /src/cprnc +COPY docker/entrypoint.sh /entrypoint.sh + +RUN SKIP_ENTRYPOINT=true source /entrypoint.sh \ + && CPRNC_DIR=/src/cprnc build_cprnc + +FROM spack AS main + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + vim \ + neovim \ + && rm -rf /var/lib/apt/lists/* + +COPY test-requirements.txt /requirements.txt + +RUN curl -LsSf https://astral.sh/uv/install.sh | sh \ + && source ~/.local/bin/env \ + && uv python install 3.10 \ + && uv venv \ + && source .venv/bin/activate \ + && uv pip install -r /requirements.txt + +COPY docker/entrypoint.sh /entrypoint.sh + +RUN SKIP_ENTRYPOINT=true source /entrypoint.sh \ + && mkdir -p /root/storage/{tools,inputdata,cases,timings,archive,baselines} \ + && download_input_data + +COPY --from=cprnc /root/storage/tools/cprnc /root/storage/tools/cprnc +COPY docker/.cime /root/.cime + +ENTRYPOINT ["/entrypoint.sh"] diff --git a/docker/cime.yaml b/docker/cime.yaml deleted file mode 100644 index 52d58039fc1..00000000000 --- a/docker/cime.yaml +++ /dev/null @@ -1,40 +0,0 @@ -name: base -channels: - - conda-forge -dependencies: -# development tools - - cmake<4.0 # some libraries still require 3.5.x - - make - - wget - - curl - - subversion - - m4 - - pkg-config - - vim - - rsync - - openssh - - tree -# python packages - - pytest - - pytest-cov - - pyyaml -# libraries - - lapack - - blas - # Testing hdf5 is out of scope for CIME - # - hdf5<1.14.0=*openmpi* - - netcdf-fortran=*=*openmpi* - - libpnetcdf=*=*openmpi* - - esmf=*=*openmpi* - - openmpi - - gcc_linux-64 - - gxx_linux-64 - - gfortran_linux-64 - - gcc=12.* - - gxx=12.* - - gfortran=12.* - - compilers - - c-compiler - - cxx-compiler - - fortran-compiler -prefix: /opt/conda diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index 5e78a7c5ccf..d11c2373484 100755 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -1,58 +1,85 @@ #!/bin/bash -export USER=root -export LOGNAME=root -export USER_ID=${USER_ID:-1000} -export GROUP_ID=${GROUP_ID:-1000} +# Set up basic user, logname, and default group/user IDs +export USER="$(id -nu)" +export LOGNAME="${USER}" +# Set static home path where .cime exists and container entrypoint options SKIP_ENTRYPOINT="${SKIP_ENTRYPOINT:-false}" +STORAGE_DIR="${HOME}/storage" -function fix_mct_makefiles() { - fix_arflags "${1}/mct/Makefile" - fix_arflags "${1}/mpeu/Makefile" - fix_arflags "${1}/mpi-serial/Makefile" +# Build the cprnc tool from CIME sources +function build_cprnc() { + cprnc_dir="${CPRNC_DIR:-${PWD}/CIME/non_py/cprnc}" + tools_dir="${STORAGE_DIR}/tools" + + if [[ ! -e "${cprnc_dir}" ]]; then + echo "CPRNC path does not exist. Change to CIME's root directory." + exit 1 + fi + + pushd "$(mktemp -d)" || exit 1 + + cmake -S "${cprnc_dir}" -B . + + make + + [[ ! -e "${tools_dir}" ]] && mkdir -p "${tools_dir}" + + # Needs to be copied into the machines configured tool path + cp cprnc "${tools_dir}/cprnc" + + popd || exit 1 } -function fix_arflags() { - if [[ ! -e "${1}.bak" ]]; then - echo "Fixing AR variable in ${1}" - sed -i".bak" "s/\$(AR)/\$(AR) \$(ARFLAGS)/g" "${1}" - fi +# Download input data needed for model setup +# required for grid generation tests +function download_input_data() { + mkdir -p "${STORAGE_DIR}/inputdata/cpl/gridmaps/oQU240" + mkdir -p "${STORAGE_DIR}/inputdata/share/domains" + + wget -O "${STORAGE_DIR}/inputdata/cpl/gridmaps/oQU240/map_oQU240_to_ne4np4_aave.160614.nc" \ + https://portal.nersc.gov/project/e3sm/inputdata/cpl/gridmaps/oQU240/map_oQU240_to_ne4np4_aave.160614.nc + + wget -O "${STORAGE_DIR}/inputdata/share/domains/domain.ocn.ne4np4_oQU240.160614.nc" \ + https://portal.nersc.gov/project/e3sm/inputdata/share/domains/domain.ocn.ne4np4_oQU240.160614.nc + + wget -O "${STORAGE_DIR}/inputdata/share/domains/domain.lnd.ne4np4_oQU240.160614.nc" \ + https://portal.nersc.gov/project/e3sm/inputdata/share/domains/domain.lnd.ne4np4_oQU240.160614.nc } -if [[ "${SKIP_ENTRYPOINT}" == "false" ]]; then + +# Link correct config_machines file based on CIME_MODEL, also set ESMFMKFILE for cesm +function link_config_machines() { if [[ "${CIME_MODEL}" == "e3sm" ]]; then - ln -sf /home/cime/.cime/config_machines.v2.xml /home/cime/.cime/config_machines.xml + ln -sf "${HOME}/.cime/config_machines.v2.xml" "${HOME}/.cime/config_machines.xml" elif [[ "${CIME_MODEL}" == "cesm" ]]; then - export ESMFMKFILE=/opt/conda/envs/cesm/lib/esmf.mk - - ln -sf /home/cime/.cime/config_machines.v3.xml /home/cime/.cime/config_machines.xml + ln -sf "${HOME}/.cime/config_machines.v3.xml" "${HOME}/.cime/config_machines.xml" fi +} - if [[ "${USER_ID}" == "0" ]]; then - cp -rf /home/cime/.cime /root/ - - git config --global --add safe.directory "*" - - exec "${@}" - else - groupmod -g "${GROUP_ID}" -n cime ubuntu - usermod -d /home/cime -u "${USER_ID}" -g "${GROUP_ID}" -l cime ubuntu +export PATH=/opt/spack-envs/view/bin:$PATH +export PKG_CONFIG_PATH=/opt/spack-envs/view/lib/pkgconfig +export LD_LIBRARY_PATH=/opt/spack-envs/view/lib +export ESMFMKFILE=/opt/spack-envs/view/lib/esmf.mk - chown -R cime:cime /home/cime +if [[ "${CI:-false}" == "true" ]]; then + cp -rf /root/.cime "${HOME}" +fi - if [[ -n "${SRC_PATH}" ]] && [[ -e "${SRC_PATH}" ]]; then - chown -R cime:cime "${SRC_PATH}" +link_config_machines - git config --global --add safe.directory "*" - fi +# Allow git to operate in any directory, for container/dev scenarios +if [[ -e "${PWD}/.git" ]]; then + git config --global --add safe.directory "*" +fi - { - echo "source /opt/conda/etc/profile.d/conda.sh" - echo "conda activate base" - } > /home/cime/.bashrc +if [[ "${CI:-false}" == "false" ]] && [[ "${SKIP_ENTRYPOINT}" == "false" ]]; then + source ${HOME}/.local/bin/env + source ${HOME}/.venv/bin/activate +fi - gosu "${USER_ID}" "${@}" - fi +if [[ "${SKIP_ENTRYPOINT}" == "false" ]]; then + exec "${@}" fi diff --git a/docker/spack.yaml b/docker/spack.yaml new file mode 100644 index 00000000000..a069663508b --- /dev/null +++ b/docker/spack.yaml @@ -0,0 +1,18 @@ +spack: + specs: + - hdf5@1.12.3+cxx+fortran+hl+mpi+shared %gcc@13.3.0 + - netcdf-c@4.9.2+mpi %gcc@13.3.0 + - netcdf-fortran@4.6.1 %gcc@13.3.0 + - parallel-netcdf@1.12.3 %gcc@13.3.0 + - esmf@8.8.1+mpi+netcdf~pnetcdf~external-parallelio %gcc@13.3.0 + concretizer: + unify: when_possible + reuse: true + targets: + granularity: generic + host_compatible: false + packages: + all: + target: [x86_64_v3] + view: + default: /opt/spack-envs/view diff --git a/test-requirements.txt b/test-requirements.txt index d4abdec4d0f..9d22f450869 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1 +1,3 @@ evv4esm +pytest +pytest-cov