Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
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/
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
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,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}
}
```
Loading