Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
5e132db
Update pyproject.toml
alperaltuntas Jul 7, 2025
127d75c
Update submodules
manishvenu Jul 9, 2025
1893527
Update Python
manishvenu Jul 9, 2025
5f02609
Create CONTRIBUTING.md
alperaltuntas Jul 24, 2025
8325a3b
Update mom6_bathy commit
manishvenu Aug 21, 2025
c4a2e8d
update cesm tag in ci test (#16)
alperaltuntas Oct 3, 2025
52cea7e
Update m6b (#17)
manishvenu Oct 7, 2025
6ae49ac
NTasks & Timestep Changes suggested by Mike (#18)
manishvenu Oct 10, 2025
70401f4
Bug
manishvenu Oct 10, 2025
2818e98
add libxml2 dependency (#24)
alperaltuntas Nov 13, 2025
2ef79c2
Fix case generation for standard MOM6 grids. (#23)
alperaltuntas Nov 13, 2025
5e32798
improvements in _retrieve_domains_and_resolutions
alperaltuntas Nov 15, 2025
79280c5
Add custom runoff grid
alperaltuntas Nov 17, 2025
b459514
Bump m6b
manishvenu Nov 18, 2025
d0e5700
Merge branch 'main' of https://github.com/ESMCI/visualCaseGen
manishvenu Nov 18, 2025
3d9ba82
bump m6b
manishvenu Nov 19, 2025
dfc1fae
introduce an initialization module
alperaltuntas Nov 22, 2025
9937cc6
Merge remote-tracking branch 'altuntas/add_custom_rof_grid' into rof_…
alperaltuntas Nov 22, 2025
eee8995
improve _process_domain_constraints and update relational constraints
alperaltuntas Nov 23, 2025
509de14
add relational constraints for runoff grid
alperaltuntas Nov 23, 2025
11f46c3
add valid options property to ConfigVar
alperaltuntas Nov 23, 2025
1bf3850
allow all guarded child stages' guards to be false.
alperaltuntas Dec 4, 2025
e32a9b2
introduce retrieve_maps and get_mesh_path mehods in CIME_interface
alperaltuntas Dec 4, 2025
6be5ccf
Introduce runoff to ocean mapping stage
alperaltuntas Dec 4, 2025
1abff01
specify xesmf version in environment.yml
alperaltuntas Dec 4, 2025
713c856
Merge branch 'main' into rof_mapping_fixes
alperaltuntas Dec 4, 2025
c44cf05
relax not_compset constraint for default grids
alperaltuntas Dec 4, 2025
7bdd9b4
update test_custom_mom6_grid.py for new rof mapping stage
alperaltuntas Dec 4, 2025
ae3b880
add rof to ocn mapping tests
alperaltuntas Dec 8, 2025
662b8e9
update mom6 version for latest mapping changes
alperaltuntas Dec 8, 2025
34a9c1b
fix rof map test
alperaltuntas Dec 8, 2025
466b55a
Merge pull request #26 from ESMCI/rof_mapping_fixes
alperaltuntas Dec 11, 2025
db9316b
Add is_default parameter to function call
alperaltuntas Dec 12, 2025
c362f49
Minor fixes in runoff ocn mapping (#28)
alperaltuntas Dec 15, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
uses: actions/checkout@v4
with:
repository: alperaltuntas/CESM
ref: cesm3_0_beta03_gui
ref: cesm3_0_beta06_gui
path: CESM
#submodules: recursive

Expand Down
37 changes: 37 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Contributing to visualCaseGen

Thank you for your interest in contributing to visualCaseGen!

We welcome contributions that fix bugs, introduce new features, and enhance the usability, robustness, or performance of the tool. Here's how you can get involved:

## Getting Started

Fork the repository and clone your fork.

Follow the instructions in the visualCaseGen documentation to set up your local visualCaseGen environment: https://esmci.github.io/visualCaseGen/

Create a new branch for your feature or bugfix.

## What You Can Contribute

- Bug fixes and issue resolutions
- Enhancements to the GUI or core functionality
- Improved documentation or examples
- Code cleanup and test coverage

## Guidelines

- Follow the existing code style and structure.
- Include clear commit messages.
- Test your changes before submitting.
- Add tests if you introduce new features.
- Open a Pull Request with a short summary of your changes.
- If addressing an open issue, mention it in your PR (e.g., Closes #42).

## Reporting Issues

Please use the GitHub Issues page to report bugs or suggest features. When reporting:

- Include the version of visualCaseGen you used and details about your system environment.
- Provide clear steps to reproduce the issue.
- Include screenshots or logs if helpful.
5 changes: 5 additions & 0 deletions ProConPy/config_var.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,11 @@ def update_options_validities(self):

return validities_changed

@property
def valid_options(self):
"""Returns the list of valid options for this variable."""
return [opt for opt in self._options if self._options_validities.get(opt, False)]

def _refresh_widget_options(self):
"""Refresh the widget options list based on information in the current self._options_validities."""

Expand Down
46 changes: 27 additions & 19 deletions ProConPy/stage.py
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ def _proceed(self):
return

# Display the child stage and its siblings by appending them to the current stage's widget
if self.has_children():
if self.has_children() and next_stage.is_descendant_of(self):
self._widget.add_child_stages(first_child=next_stage)

# Proceed the csp solver before enabling the next stage
Expand All @@ -378,20 +378,24 @@ def get_next(self, full_dfs=False):
The next stage to visit, if found. Otherwise, None.
"""

if self.has_children():
return self._get_child_to_enable(full_dfs)
elif self._right is not None:
# First try to get a child stage to enable
if (child_to_enable := self._get_child_to_enable(full_dfs)) is not None:
return child_to_enable

# No child stage to enable. Try to get the right sibling.
if self._right is not None:
return self._right
else: # Backtrack
ancestor = self._parent
while ancestor is not None:
if ancestor._right is not None and (
full_dfs or not ancestor.has_condition()
):
return ancestor._right
else:
ancestor = ancestor._parent
return None

# No child or right sibling. Backtrack to find the next stage.
ancestor = self._parent
while ancestor is not None:
if ancestor._right is not None and (
full_dfs or not ancestor.has_condition()
):
return ancestor._right
else:
ancestor = ancestor._parent
return None

def _get_child_to_enable(self, full_dfs):
"""Determine the child stage to activate.
Expand All @@ -401,6 +405,9 @@ def _get_child_to_enable(self, full_dfs):
full_dfs : bool
If True, visit all the stages in the stage tree. Otherwise, skip stages whose guards
are not satisfied."""

if self.has_children() is False:
return None

child_to_activate = None

Expand All @@ -412,18 +419,19 @@ def _get_child_to_enable(self, full_dfs):
child_to_activate is None
), "Only one child stage can be activated at a time."
child_to_activate = child

if child_to_activate is None:
# No child guard's condition is satisfied.
# Let the caller handle this case (by backtracking).
return None
else:
# If children are not guards, the first child is activated.
# Note the remaining children will be activated in sequence by their siblings.
child_to_activate = self._children[0]

# If the child to activate is a Guard, return it's first child
if child_to_activate.has_condition():
return child_to_activate._children[0]

assert (
child_to_activate is not None
), "At least one child stage must be activated."
child_to_activate = child_to_activate._children[0]

return child_to_activate

Expand Down
3 changes: 2 additions & 1 deletion environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ channels:

dependencies:
- python>=3.11.10,<3.12
#- xesmf
- libxml2>=2.13,<2.14
- xesmf>=0.8.10,<0.9
- pip
- pip:
- -e ./external/mom6_bathy/
Expand Down
2 changes: 1 addition & 1 deletion external/ipyfilechooser
2 changes: 1 addition & 1 deletion external/mom6_bathy
Submodule mom6_bathy updated 146 files
6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "visualCaseGen"
version = "0.1.1"
version = "0.1.3"
authors = [
{ name = "Alper Altuntas, NCAR", email = "altuntas@ucar.edu" }
]
Expand All @@ -9,7 +9,7 @@ readme = "README.md"
license = { file = "LICENSE.md" }
classifiers = [
"Intended Audience :: Science/Research",
"License :: OSI Approved :: LGPL",
"License :: OSI Approved :: Apache 2.0 License",
"Programming Language :: Python",
"Framework :: Jupyter"
]
Expand All @@ -35,4 +35,4 @@ requires = ["setuptools>=42", "wheel"]
build-backend = "setuptools.build_meta"

[tool.setuptools]
packages = ["ProConPy", "visualCaseGen"]
packages = ["ProConPy", "visualCaseGen"]
6 changes: 3 additions & 3 deletions tests/1_unit/test_cores_case_creator.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ def test_calc_cores_based_on_grid_cases():
assert CaseCreator._calc_cores_based_on_grid(33) == 1

# Test ideal cores amount
assert CaseCreator._calc_cores_based_on_grid(800*128) == 128
assert CaseCreator._calc_cores_based_on_grid(300*128) == 128

assert CaseCreator._calc_cores_based_on_grid(800*32) == 128
assert CaseCreator._calc_cores_based_on_grid(300*32) == 128

assert CaseCreator._calc_cores_based_on_grid(740 * 780) == 768
assert CaseCreator._calc_cores_based_on_grid(740 * 780) == 2048



107 changes: 107 additions & 0 deletions tests/3_system/test_custom_compset_std_grid.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
#!/usr/bin/env python3

import pytest
import shutil
import os
from pathlib import Path
import tempfile
import time

from ProConPy.config_var import ConfigVar, cvars
from ProConPy.stage import Stage
from ProConPy.csp_solver import csp
from visualCaseGen.cime_interface import CIME_interface
from visualCaseGen.initialize_configvars import initialize_configvars
from visualCaseGen.initialize_widgets import initialize_widgets
from visualCaseGen.initialize_stages import initialize_stages
from visualCaseGen.specs.options import set_options
from visualCaseGen.specs.relational_constraints import get_relational_constraints
from visualCaseGen.custom_widget_types.mom6_bathy_launcher import MOM6BathyLauncher
from visualCaseGen.custom_widget_types.case_creator_widget import CaseCreatorWidget
from tests.utils import safe_create_case


# do not show logger output
import logging

logger = logging.getLogger()
logger.setLevel(logging.CRITICAL)

base_temp_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "temp"))


def test_custom_compset_std_grid():
"""Configure a custom compset with a standard grid: 2000_DATM%JRA_SLND_CICE%PRES_MOM6_SROF_SGLC_WW3. Progress through the stages
until the launch stage is reached."""

ConfigVar.reboot()
Stage.reboot()
cime = CIME_interface()
initialize_configvars(cime)
initialize_widgets(cime)
initialize_stages(cime)
set_options(cime)
csp.initialize(cvars, get_relational_constraints(cvars), Stage.first())

# At initialization, the first stage should be enabled
assert Stage.first().enabled
cvars['COMPSET_MODE'].value = 'Custom'

# CCOMPSET_MODE is the only variable in the first stage, so assigning a value to it should disable the first stage
assert not Stage.first().enabled

# The next stge is Custom Component Set, whose first child is Model Time Period
assert Stage.active().title.startswith('Time Period')
cvars['INITTIME'].value = '1850'

cvars['COMP_OCN'].value = "mom"
cvars['COMP_ICE'].value = "sice"
cvars['COMP_ATM'].value = "cam"
cvars['COMP_ROF'].value = "mosart"
cvars['COMP_LND'].value = "clm"
cvars['COMP_WAV'].value = "swav"
cvars['COMP_GLC'].value = "sglc"


assert Stage.active().title.startswith('Component Physics')

cvars['COMP_ATM_PHYS'].value = "CAM60"
cvars['COMP_LND_PHYS'].value = "CLM60"

assert Stage.active().title.startswith('Component Options')

cvars['COMP_ATM_OPTION'].value = "1PCT"
cvars['COMP_LND_OPTION'].value = "BGC"
cvars['COMP_OCN_OPTION'].value = "(none)"
cvars['COMP_ICE_OPTION'].value = "(none)"
cvars['COMP_ROF_OPTION'].value = "(none)"

assert Stage.active().title.startswith('2. Grid')

cvars['GRID_MODE'].value = 'Standard'
cvars['GRID'].value = 'f09_t232'

assert Stage.active().title.startswith('3. Launch')
launch_stage = Stage.active()

with tempfile.TemporaryDirectory(dir=base_temp_dir) as temp_case_path:
pass # immediately remove the random temporary directory,
# which will become the caseroot directory below

cvars["CASEROOT"].value = temp_case_path

case_creator = launch_stage._widget._main_body.children[-1]
assert isinstance(case_creator, CaseCreatorWidget)

cvars["PROJECT"].value = "12345"

# *Click* the create_case button
safe_create_case(cime.srcroot, case_creator)

# sleep for a bit to allow the case to be created
time.sleep(5)

# remove the caseroot directory
shutil.rmtree(temp_case_path)


5 changes: 4 additions & 1 deletion tests/3_system/test_custom_mom6_grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,10 @@ def test_custom_mom6_grid():
assert Stage.active().title.startswith("Simple Initial Conditions")
cvars["T_REF"].value = 10.0

# Since land grid gets set automatically, we should be in the Launch stage:
# Since land grid and runoff grid get set automatically, we should be in the runoff to ocn mapping:
assert Stage.active().title.startswith("Runoff to Ocean Mapping")
cvars["ROF_OCN_MAPPING_STATUS"].value = "skip"

assert Stage.active().title.startswith("3. Launch")
launch_stage = Stage.active()

Expand Down
9 changes: 9 additions & 0 deletions tests/3_system/test_f2000_custom_grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,9 @@ def construct_custom_res_from_std_grids(cime):
assert Stage.active().title.startswith("Land Grid")
cvars["CUSTOM_LND_GRID"].value = "0.9x1.25"

assert Stage.active().title.startswith("Runoff Grid")
cvars["CUSTOM_ROF_GRID"].value = "r05"

assert Stage.active().title.startswith("3. Launch")
launch_stage = Stage.active()

Expand Down Expand Up @@ -248,6 +251,9 @@ def construct_custom_res_from_modified_clm_grid(cime):
# click the "Run Surface Data Modifier" button
fsurdat_modifier_launcher._on_launch_clicked(b=None)

assert Stage.active().title.startswith("Runoff Grid")
cvars["CUSTOM_ROF_GRID"].value = "r05"

assert Stage.active().title.startswith("3. Launch")
launch_stage = Stage.active()

Expand Down Expand Up @@ -364,6 +370,9 @@ def construct_custom_res_from_new_mom6_grid_modified_clm_grid(cime):
# click the "Run Surface Data Modifier" button
fsurdat_modifier_launcher._on_launch_clicked(b=None)

assert Stage.active().title.startswith("Runoff Grid")
cvars["CUSTOM_ROF_GRID"].value = "r05"

assert Stage.active().title.startswith("3. Launch")
launch_stage = Stage.active()

Expand Down
6 changes: 6 additions & 0 deletions tests/3_system/test_fhist_custom_grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@ def construct_custom_res_from_std_grids(cime):
assert Stage.active().title.startswith("Land Grid")
cvars["CUSTOM_LND_GRID"].value = "0.9x1.25"

assert Stage.active().title.startswith("Runoff Grid")
cvars["CUSTOM_ROF_GRID"].value = "r05"

assert Stage.active().title.startswith("3. Launch")
launch_stage = Stage.active()

Expand Down Expand Up @@ -218,6 +221,9 @@ def construct_custom_res_from_modified_clm_grid(cime):
# click the "Run Surface Data Modifier" button
fsurdat_modifier_launcher._on_launch_clicked(b=None)

assert Stage.active().title.startswith("Runoff Grid")
cvars["CUSTOM_ROF_GRID"].value = "r05"

assert Stage.active().title.startswith("3. Launch")
launch_stage = Stage.active()

Expand Down
Loading
Loading