Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 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
4bb1834
Replace ccs_config edits with xmlchange (#20)
manishvenu Jan 13, 2026
7974208
bump mom6_bathy version
manishvenu Feb 18, 2026
ca1d5f3
Bump m6b (#30)
manishvenu Feb 19, 2026
04b14cc
Add DOI badge to README
alperaltuntas Mar 20, 2026
856c480
Rename git module
manishvenu Mar 26, 2026
4774fba
Revert "Rename git module"
manishvenu Mar 26, 2026
254ab70
Update Python Version and change mom6_bathy to mom6_forge (#37)
manishvenu Apr 2, 2026
aee5f90
Update to CESM3 beta08 and address incompability issues (#38)
alperaltuntas Apr 6, 2026
8190dd4
find cime version in tag, allowing prefix or suffixes (#39)
hkershaw-brown Jun 12, 2026
e1f1397
Minor optimizations in csp_solver (#35)
alperaltuntas Jun 23, 2026
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
14 changes: 11 additions & 3 deletions .github/workflows/ci-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,20 @@ on:

jobs:
ci_tests:
name: visualCaseGen CI tests
name: visualCaseGen CI tests (Python ${{ matrix.python-version }})
runs-on: "ubuntu-latest"
strategy:
fail-fast: false
matrix:
python-version: ["3.11", "3.12"]
defaults:
run:
shell: bash -el {0}
steps:

- name: Configure Git Identity
run: |
git config --global user.name "github-actions"
git config --global user.email "github-actions@github.com"
- name: Install xmllint
run: sudo apt-get install -y libxml2-utils
- name: Checkout VisualCaseGen to temp folder
Expand All @@ -29,7 +36,7 @@ jobs:
uses: actions/checkout@v4
with:
repository: alperaltuntas/CESM
ref: cesm3_0_beta03_gui
ref: cesm3_0_beta08_gui
path: CESM
#submodules: recursive

Expand All @@ -53,6 +60,7 @@ jobs:
- name: Create visualCaseGen conda env
run: |
cd CESM/visualCaseGen/
sed -i "s/python>=3.11.10/python=${{ matrix.python-version }}.*/" environment.yml
conda env create --file environment.yml
conda activate visualCaseGen

Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,6 @@ internal/sdb_*.json

# netcdf files
*.nc

# TopoLibrary files
mom6_bathy_notebooks/TopoLibrary/
6 changes: 3 additions & 3 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[submodule "external/mom6_bathy"]
path = external/mom6_bathy
url = https://github.com/NCAR/mom6_bathy.git
[submodule "external/mom6_forge"]
path = external/mom6_forge
url = https://github.com/NCAR/mom6_forge.git
[submodule "external/ipyfilechooser"]
path = external/ipyfilechooser
url = https://github.com/alperaltuntas/ipyfilechooser.git
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
57 changes: 29 additions & 28 deletions ProConPy/csp_solver.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import logging
from collections import deque
from z3 import Solver, Optimize, sat, unsat, Or
from z3 import BoolRef, Int
from z3 import z3util
Expand Down Expand Up @@ -69,11 +70,11 @@ def _refresh_solver(self):
be more efficient than the initial approach of using push/pop to manage the solver.
"""
self._solver.reset()
self._solver.add([asrt for asrt, _ in self._relational_constraints.items()])
self._solver.add(list(self._relational_constraints))
for scope in self._past_assignment_assertions:
self._solver.add([asrt for _, asrt in scope.items()])
self._solver.add(list(scope.values()))
for scope in self._past_options_assertions:
self._solver.add([asrt for _, asrt in scope.items()])
self._solver.add(list(scope.values()))

def initialize(self, cvars, relational_constraints, first_stage):
"""Initialize the CSP solver with relational constraints. The relational constraints are
Expand Down Expand Up @@ -432,9 +433,9 @@ def retrieve_error_msg(self, var, new_value):
s.set(":core.minimize", True)
# apply past assertions
for stage in self._past_assignment_assertions:
s.add([asrt for _, asrt in stage.items()])
s.add(list(stage.values()))
for stage in self._past_options_assertions:
s.add([asrt for _, asrt in stage.items()])
s.add(list(stage.values()))
# apply current assertions
self.apply_assignment_assertions(s, exclude_var=var)
self.apply_options_assertions(s)
Expand Down Expand Up @@ -567,28 +568,23 @@ def _refresh_options_validities(self, var):
"""

# Queue of variables to be visited
queue = [neig for neig in self._cgraph[var] if neig.has_options()]
queue = deque([neig for neig in self._cgraph[var] if neig.has_options()])

# Set of all variables that have been queued
queued = {var} | set(queue)

# Traverse the constraint graph to refresh the options validities of all possibly affected variables
while queue:

# Pop the first variable from the queue
var = queue.pop(0)
var = queue.popleft()
logger.debug("Refreshing options validities of %s.", var)

# Update the validities of the variable and extend the queue if necessary
validities_changed = var.update_options_validities()
if validities_changed:
extension = [
neig
for neig in self._cgraph[var]
if neig.has_options() and neig not in queued
]
queue.extend(extension)
queued.update(extension)
if not var.update_options_validities():
continue

for neig in self._cgraph[var]:
if neig.has_options() and neig not in queued:
queue.append(neig)
queued.add(neig)

def apply_assignment_assertions(self, solver, exclude_var=None, exclude_vars=None):
"""Apply the assignment assertions to the given solver. The assignment assertions are
Expand All @@ -611,11 +607,12 @@ def apply_assignment_assertions(self, solver, exclude_var=None, exclude_vars=Non
), "Cannot provide both exclude_var and exclude_vars."

if exclude_vars:
exclude_ids = {id(v) for v in exclude_vars}
solver.add(
[
asrt
for var, asrt in self._assignment_assertions.items()
if not any(exclude_var is var for exclude_var in exclude_vars)
if id(var) not in exclude_ids
]
)
else:
Expand All @@ -627,7 +624,7 @@ def apply_assignment_assertions(self, solver, exclude_var=None, exclude_vars=Non
]
)

def apply_options_assertions(self, solver, exclude_vars=[]):
def apply_options_assertions(self, solver, exclude_vars=()):
"""Apply the options assertions to the given solver. The options assertions are the
assertions that are determined at the current stage but are the options for the variables
of the future stages. The options assertions are added to the temporary assertions container
Expand All @@ -641,13 +638,17 @@ def apply_options_assertions(self, solver, exclude_vars=[]):
exclude_vars : list or set
A list or set of variables for which the options assertions are not to be applied.
"""
solver.add(
[
asrt
for var, asrt in self._options_assertions.items()
if not any(exclude_var is var for exclude_var in exclude_vars)
]
)
if not exclude_vars:
solver.add(list(self._options_assertions.values()))
else:
exclude_ids = {id(v) for v in exclude_vars}
solver.add(
[
asrt
for var, asrt in self._options_assertions.items()
if id(var) not in exclude_ids
]
)

def register_options(self, var, new_options):
"""Register the new options for the given variable. The registry is made to the temporary
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
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
[![DOI](https://joss.theoj.org/papers/10.21105/joss.09130/status.svg)](https://doi.org/10.21105/joss.09130)

# visualCaseGen

Welcome to **visualCaseGen**, an intuitive graphical user interface (GUI) designed to simplify the workflow of creating Community Earth System Model v.3 (CESM3) cases. With visualCaseGen, users can effortlessly explore and define component sets (compsets), select or customize grid resolutions, and prepare their CESM cases, all through an interactive and user-friendly platform that runs on Jupyter notebooks.
Expand All @@ -18,3 +20,25 @@ Welcome to **visualCaseGen**, an intuitive graphical user interface (GUI) design
## User Manual

For detailed instructions on how to use visualCaseGen, including setup and configuration guidance, please refer to the [User Manual](https://esmci.github.io/visualCaseGen/).

## Citing

If you use visualCaseGen in your research, please cite it as follows:

Altuntas, A., Simpson, I.R., Bachman, S.D., Venumuddula, M., Levis, S., Dobbins, B., Sacks, W.J. and
Danabasoglu, G., 2026. "visualCaseGen: An SMT-based Experiment Configurator for Community Earth System
Model." Journal of Open Source Software, 11(119), p.9130. doi: [10.21105/joss.09130](https://doi.org/10.21105/joss.09130).

BibTeX entry:

```bibtex
@article{altuntas2026visualcasegen,
title={visualCaseGen: An SMT-based Experiment Configurator for Community Earth System Model},
author={Altuntas, Alper and Simpson, Isla R and Bachman, Scott D and Venumuddula, Manish and Levis, Samuel and Dobbins, Brian and Sacks, William J and Danabasoglu, Gokhan},
journal={Journal of Open Source Software},
volume={11},
number={119},
pages={9130},
year={2026}
}
```
12 changes: 6 additions & 6 deletions docs/grid.rst
Original file line number Diff line number Diff line change
Expand Up @@ -64,23 +64,23 @@ Ocean Grid
For the ocean grid, if MOM6 is selected as the ocean model, you can either select a standard
ocean grid or create a new MOM6 grid. When creating a new MOM6 grid, you'll specify parameters
such as grid extent and resolution, after which you'll be directed to a separate notebook that
uses the `mom6_bathy` tool to generate the new grid and bathymetry.
uses the `mom6_forge` tool to generate the new grid and bathymetry.

If using a standard ocean grid, select one from the list compatible with your chosen compset
and atmosphere grid. If creating a new MOM6 grid, complete the required parameters, then proceed
to launch the `mom6_bathy` tool for final customization.
to launch the `mom6_forge` tool for final customization.

.. image:: assets/Stage2_6.png

After specifying all ocean grid parameters, click `Launch mom6_bathy`. This will open an
After specifying all ocean grid parameters, click `Launch mom6_forge`. This will open an
auto-generated Jupyter notebook where you can fine-tune the ocean grid, topography, and vertical grid.
You can then generate the corresponding MOM6 input files, which will be saved under `ocnice` subdirectory
within the directory you specified earlier for saving the new grid files.
For more details on mom6_bathy, refer to its documentation: https://ncar.github.io/mom6_bathy/
For more details on mom6_forge, refer to its documentation: https://ncar.github.io/mom6_forge/

.. note:: If the `mom6_bathy` notebook doesn't open automatically, make sure that your browser allows
.. note:: If the `mom6_forge` notebook doesn't open automatically, make sure that your browser allows
pop-ups from visualCaseGen. If the notebook still doesn't open, you can manually launch it by
navigating to the `mom6_bathy_notebooks/` directory in your visualCaseGen installation and opening
navigating to the `mom6_forge_notebooks/` directory in your visualCaseGen installation and opening
the notebook corresponding to your custom grid.

Ocean Initial conditions
Expand Down
6 changes: 3 additions & 3 deletions docs/installation.rst
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Installation
======================================

visualCaseGen is presently bundled with a CESM distribution (fork) based on cesm3_0_beta06. The following
visualCaseGen is presently bundled with a CESM distribution (fork) based on cesm3_0_beta08. The following
instructions guide you through obtaining and installing this specific CESM distribution with visualCaseGen.

Prerequisite
Expand Down Expand Up @@ -31,8 +31,8 @@ CESM may take some time.

.. code-block:: bash

git clone https://github.com/alperaltuntas/cesm.git -b cesm3_0_beta06_gui cesm3_0_beta06_gui
cd cesm3_0_beta06_gui
git clone https://github.com/alperaltuntas/cesm.git -b cesm3_0_beta08_gui cesm3_0_beta08_gui
cd cesm3_0_beta08_gui
./bin/git-fleximod update

This will download the required CESM version, including visualCaseGen.
Expand Down
Loading
Loading