Skip to content

Commit

Permalink
Merge branch 'main' of github.com:CWorthy-ocean/C-Star into unit-tests
Browse files Browse the repository at this point in the history
  • Loading branch information
NoraLoose committed Oct 15, 2024
2 parents f8d33e0 + 5cbc2cb commit 57b1adc
Show file tree
Hide file tree
Showing 21 changed files with 2,650 additions and 1,372 deletions.
51 changes: 16 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
[![codecov](https://codecov.io/gh/CWorthy-ocean/C-Star/graph/badge.svg?token=HAPZGL2LWF)](https://codecov.io/gh/CWorthy-ocean/C-Star)
[![Documentation Status](https://readthedocs.org/projects/c-star/badge/?version=latest)](https://c-star.readthedocs.io/en/latest/?badge=latest)


# Installation

## Installation from GitHub

To obtain the latest development version, clone [this repository](https://github.com/CWorthy-ocean/C-Star):

```
Expand All @@ -28,46 +29,26 @@ Finally, install `C-Star` in the same environment:
pip install -e .
```

# Using C-Star:
It is recommended that first-time users see the example notebook `examples/cstar_example_notebook.ipynb`. A summary is provided here:

## Overview of C-Star structures
- A Case (`cstar.Case`) is the primary object of C-Star. It contains all the necessary information for a user to run a reproducable Earth system simulation.
- A Case is built from Components (`cstar.base.Component`), each representing a specific configuration of a model of one part of the overall system being simulated. In this notebook we'll be working with an ocean circulation component and a biogeochemistry component.
- A Component object, meanwhile, consists of, at least, a base model (`cstar.base.BaseModel`), and optionally additional code (`cstar.base.AdditionalCode`), input datasets (`cstar.base.InputDataset`), and discretization information needed to run the base model in the specific configuration in question. In the simplest scenario, we can imagine a Case consisting of a single Component which is just a base model in an bundled example configuration (like an ocean double gyre) with itsings run in serial withtical initial and forcing data (i.e. no additional code, input datasets, or parallelization information needed).
- You can find more information on C-Star `Case`, `Component`, `BaseModel`, `AdditionalCode`, and `InputDataset` objects by querying, e.g., `cstar.base.Component?`.
## Run the tests

## Constructing a C-Star Case:
A Case can be instantiated in one of two ways:

- using the standard constructor (after manually constructing the Component objects that will make up the Case):

```python
my_case = cstar.Case(
list_of_component_objects,
name='case_name',
caseroot='/path/to/where/case/will/be/run',
)
Before running the tests, you can activate the conda environment created in the previous section:
```
conda activate cstar_env
```

Check the installation of `C-Star` has worked by running the test suite
```
cd C-Star
pytest
```

- From a pre-defined "blueprint", using
# Getting Started

```python
my_case = cstar.Case.from_blueprint('path/to/blueprint.yaml')
```
To learn how to use `C-Star`, check out the [documentation](https://c-star.readthedocs.io/en/latest/index.html).

An example blueprint file is provided at
# Feedback and contributions

```<repository_top_level>/cstar/examples/cstar_blueprint_roms_marbl_example.yaml```
If you find a bug, have a feature suggestion, or any other kind of feedback, please start a Discussion.

## Running a C-Star Case:
Once a case has been constructed, the sequence of steps to run it is as follows:
We also accept contributions in the form of Pull Requests.

- `my_case.setup()`:
- Prompts the user to install any external codebases that cannot be located on the machine
- Downloads local copies of any input datasets and additional code described by each component's `AdditionalCode` and `InputDataset` objects that are needed to run the case to the `Case.caseroot` directory
- `my_case.build()` compiles an executable of the primary Component's BaseModel. The path to the executable is saved to the `BaseModel.exe_path` attribute
- `my_case.pre_run()` performs any pre-processing steps necessary to run the model
- `my_case.run(account_key='MY_ACCOUNT_KEY')` either executes the case or submits it to the appropriate job scheduler with the user's provided account key. If running on a machine without a scheduling system (such as a laptop), the optional `account_key` argument can be ignored. Additional arguments include `walltime='HH:MM:SS'` and `job_name='my_job_name'`
- `my_case.post_run()` performs any necessary post-processing steps to work with the output.
3 changes: 3 additions & 0 deletions cstar/additional_files/lmod_lists/derecho.lmod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
intel
netcdf
cray-mpich/8.1.25
4 changes: 4 additions & 0 deletions cstar/additional_files/lmod_lists/expanse.lmod
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
slurm sdsc DefaultModules shared
cpu/0.15.4 intel/19.1.1.217 mvapich2/2.3.4
netcdf-c/4.7.4
netcdf-fortran/4.5.3
3 changes: 3 additions & 0 deletions cstar/additional_files/lmod_lists/perlmutter.lmod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
cpu/1.0
cray-hdf5/1.12.2.9
cray-netcdf/4.9.0.9
131 changes: 76 additions & 55 deletions cstar/base/environment.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import io
import os
import sys
import platform
import importlib.util
from pathlib import Path
from typing import Optional
from contextlib import redirect_stderr, redirect_stdout

top_level_package_name = __name__.split(".")[0]
spec = importlib.util.find_spec(top_level_package_name)
Expand All @@ -18,42 +19,63 @@
_CSTAR_COMPILER: str
_CSTAR_SYSTEM: str
_CSTAR_SCHEDULER: Optional[str]
_CSTAR_ENVIRONMENT_VARIABLES: dict = {}
_CSTAR_SYSTEM_DEFAULT_PARTITION: Optional[str]
_CSTAR_SYSTEM_CORES_PER_NODE: Optional[int]
_CSTAR_SYSTEM_MEMGB_PER_NODE: Optional[int]
_CSTAR_SYSTEM_MAX_WALLTIME: Optional[str]


if (platform.system() == "Linux") and ("LMOD_DIR" in list(os.environ)):
sys.path.append(os.environ["LMOD_DIR"] + "/../init")
from env_modules_python import module

if "LMOD_SYSHOST" in list(os.environ):
sysname = os.environ["LMOD_SYSHOST"].casefold()
elif "LMOD_SYSTEM_NAME" in list(os.environ):
sysname = os.environ["LMOD_SYSTEM_NAME"].casefold()
else:
# Dynamically load the env_modules_python module using pathlib
module_path = Path(os.environ["LMOD_DIR"]).parent / "init" / "env_modules_python.py"
spec = importlib.util.spec_from_file_location("env_modules_python", module_path)
if (spec is None) or (spec.loader is None):
raise EnvironmentError(
f"Could not find env_modules_python on this machine at {module_path}"
)
env_modules = importlib.util.module_from_spec(spec)
if env_modules is None:
raise EnvironmentError(
f"No module found by importlib corresponding to spec {spec}"
)
spec.loader.exec_module(env_modules)
module = env_modules.module

sysname = os.environ.get("LMOD_SYSHOST") or os.environ.get("LMOD_SYSTEM_NAME")
if not sysname:
raise EnvironmentError(
"unable to find LMOD_SYSHOST or LMOD_SYSTEM_NAME in environment. "
+ "Your system may be unsupported"
)

module("restore")
module_stdout = io.StringIO()
module_stderr = io.StringIO()

# Load Linux Environment Modules for this machine:
with redirect_stdout(module_stdout), redirect_stderr(module_stderr):
module("reset")
with open(f"{_CSTAR_ROOT}/additional_files/lmod_lists/{sysname}.lmod") as F:
lmod_list = F.readlines()
for mod in lmod_list:
module("load", mod)
if any(
keyword in module_stderr.getvalue().casefold() for keyword in ["fail", "error"]
):
raise EnvironmentError(
"Error with linux environment modules: " + module_stderr.getvalue()
)

match sysname:
case "expanse":
sdsc_default_modules = "slurm sdsc DefaultModules shared"
nc_prerequisite_modules = "cpu/0.15.4 intel/19.1.1.217 mvapich2/2.3.4"
module("load", sdsc_default_modules)
module("load", nc_prerequisite_modules)
module("load", "netcdf-c/4.7.4")
module("load", "netcdf-fortran/4.5.3")

os.environ["NETCDFHOME"] = os.environ["NETCDF_FORTRANHOME"]
os.environ["MPIHOME"] = os.environ["MVAPICH2HOME"]
os.environ["NETCDF"] = os.environ["NETCDF_FORTRANHOME"]
os.environ["MPI_ROOT"] = os.environ["MVAPICH2HOME"]
_CSTAR_ENVIRONMENT_VARIABLES["NETCDFHOME"] = os.environ[
"NETCDF_FORTRANHOME"
]
_CSTAR_ENVIRONMENT_VARIABLES["MPIHOME"] = os.environ["MVAPICH2HOME"]
_CSTAR_ENVIRONMENT_VARIABLES["NETCDF"] = os.environ["NETCDF_FORTRANHOME"]
_CSTAR_ENVIRONMENT_VARIABLES["MPI_ROOT"] = os.environ["MVAPICH2HOME"]
_CSTAR_COMPILER = "intel"
_CSTAR_SYSTEM = "sdsc_expanse"
_CSTAR_SYSTEM = "expanse"
_CSTAR_SCHEDULER = (
"slurm" # can get this with `scontrol show config` or `sinfo --version`
)
Expand All @@ -65,21 +87,19 @@
_CSTAR_SYSTEM_MAX_WALLTIME = "48:00:00" # (hostname/cpus/mem[MB]/walltime)

case "derecho":
module("load" "intel")
module("load", "netcdf")
module("load", "cray-mpich/8.1.25")

os.environ["MPIHOME"] = "/opt/cray/pe/mpich/8.1.25/ofi/intel/19.0/"
os.environ["NETCDFHOME"] = os.environ["NETCDF"]
os.environ["LD_LIBRARY_PATH"] = (
_CSTAR_ENVIRONMENT_VARIABLES["MPIHOME"] = (
"/opt/cray/pe/mpich/8.1.25/ofi/intel/19.0/"
)
_CSTAR_ENVIRONMENT_VARIABLES["NETCDFHOME"] = os.environ["NETCDF"]
_CSTAR_ENVIRONMENT_VARIABLES["LD_LIBRARY_PATH"] = (
os.environ.get("LD_LIBRARY_PATH", default="")
+ ":"
+ os.environ["NETCDFHOME"]
+ _CSTAR_ENVIRONMENT_VARIABLES["NETCDFHOME"]
+ "/lib"
)

_CSTAR_COMPILER = "intel"
_CSTAR_SYSTEM = "ncar_derecho"
_CSTAR_SYSTEM = "derecho"
_CSTAR_SCHEDULER = (
"pbs" # can determine dynamically by testing for `qstat --version`
)
Expand All @@ -93,33 +113,33 @@
_CSTAR_SYSTEM_MAX_WALLTIME = "12:00:00" # with grep or awk

case "perlmutter":
module("load", "cpu")
module("load", "cray-hdf5/1.12.2.9")
module("load", "cray-netcdf/4.9.0.9")

os.environ["MPIHOME"] = "/opt/cray/pe/mpich/8.1.28/ofi/gnu/12.3/"
os.environ["NETCDFHOME"] = "/opt/cray/pe/netcdf/4.9.0.9/gnu/12.3/"
os.environ["PATH"] = (
_CSTAR_ENVIRONMENT_VARIABLES["MPIHOME"] = (
"/opt/cray/pe/mpich/8.1.28/ofi/gnu/12.3/"
)
_CSTAR_ENVIRONMENT_VARIABLES["NETCDFHOME"] = (
"/opt/cray/pe/netcdf/4.9.0.9/gnu/12.3/"
)
_CSTAR_ENVIRONMENT_VARIABLES["PATH"] = (
os.environ.get("PATH", default="")
+ ":"
+ os.environ["NETCDFHOME"]
+ _CSTAR_ENVIRONMENT_VARIABLES["NETCDFHOME"]
+ "/bin"
)
os.environ["LD_LIBRARY_PATH"] = (
_CSTAR_ENVIRONMENT_VARIABLES["LD_LIBRARY_PATH"] = (
os.environ.get("LD_LIBRARY_PATH", default="")
+ ":"
+ os.environ["NETCDFHOME"]
+ _CSTAR_ENVIRONMENT_VARIABLES["NETCDFHOME"]
+ "/lib"
)
os.environ["LIBRARY_PATH"] = (
_CSTAR_ENVIRONMENT_VARIABLES["LIBRARY_PATH"] = (
os.environ.get("LIBRARY_PATH", default="")
+ ":"
+ os.environ["NETCDFHOME"]
+ _CSTAR_ENVIRONMENT_VARIABLES["NETCDFHOME"]
+ "/lib"
)

_CSTAR_COMPILER = "gnu"
_CSTAR_SYSTEM = "nersc_perlmutter"
_CSTAR_SYSTEM = "perlmutter"
_CSTAR_SCHEDULER = "slurm"
_CSTAR_SYSTEM_DEFAULT_PARTITION = "regular"
_CSTAR_SYSTEM_CORES_PER_NODE = (
Expand All @@ -132,12 +152,12 @@
elif (platform.system() == "Darwin") and (platform.machine() == "arm64"):
# if on MacOS arm64 all dependencies should have been installed by conda

os.environ["MPIHOME"] = os.environ["CONDA_PREFIX"]
os.environ["NETCDFHOME"] = os.environ["CONDA_PREFIX"]
os.environ["LD_LIBRARY_PATH"] = (
_CSTAR_ENVIRONMENT_VARIABLES["MPIHOME"] = os.environ["CONDA_PREFIX"]
_CSTAR_ENVIRONMENT_VARIABLES["NETCDFHOME"] = os.environ["CONDA_PREFIX"]
_CSTAR_ENVIRONMENT_VARIABLES["LD_LIBRARY_PATH"] = (
os.environ.get("LD_LIBRARY_PATH", default="")
+ ":"
+ os.environ["NETCDFHOME"]
+ _CSTAR_ENVIRONMENT_VARIABLES["NETCDFHOME"]
+ "/lib"
)
_CSTAR_COMPILER = "gnu"
Expand All @@ -153,12 +173,12 @@
and (platform.machine() == "x86_64")
and ("LMOD_DIR" not in list(os.environ))
):
os.environ["MPIHOME"] = os.environ["CONDA_PREFIX"]
os.environ["NETCDFHOME"] = os.environ["CONDA_PREFIX"]
os.environ["LD_LIBRARY_PATH"] = (
_CSTAR_ENVIRONMENT_VARIABLES["MPIHOME"] = os.environ["CONDA_PREFIX"]
_CSTAR_ENVIRONMENT_VARIABLES["NETCDFHOME"] = os.environ["CONDA_PREFIX"]
_CSTAR_ENVIRONMENT_VARIABLES["LD_LIBRARY_PATH"] = (
os.environ.get("LD_LIBRARY_PATH", default="")
+ ":"
+ os.environ["NETCDFHOME"]
+ _CSTAR_ENVIRONMENT_VARIABLES["NETCDFHOME"]
+ "/lib"
)
_CSTAR_COMPILER = "gnu"
Expand All @@ -176,9 +196,10 @@

_CSTAR_CONFIG_FILE = _CSTAR_ROOT + "/cstar_local_config.py"
if Path(_CSTAR_CONFIG_FILE).exists():
from cstar.cstar_local_config import set_local_environment

set_local_environment()
from cstar.cstar_local_config import get_user_environment

get_user_environment()
for var, value in _CSTAR_ENVIRONMENT_VARIABLES.items():
os.environ[var] = value

################################################################################
4 changes: 2 additions & 2 deletions cstar/base/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ def _write_to_config_file(config_file_str: str) -> None:
+ "# You can safely delete this file, but C-Star may prompt you to re-install things if so."
)

base_conf_str += "\nimport os\n"
base_conf_str += "def set_local_environment():\n"
base_conf_str += "\nimport os\nfrom cstar.base.environment import _CSTAR_ENVIRONMENT_VARIABLES\n"
base_conf_str += "def get_user_environment():\n"
config_file_str = base_conf_str + config_file_str

with open(_CSTAR_CONFIG_FILE, "a") as f:
Expand Down
Loading

0 comments on commit 57b1adc

Please sign in to comment.