From c5f747b59c353bc62b4ed90e5a2b571089d0428e Mon Sep 17 00:00:00 2001
From: Ivan Kondov <ivan.kondov@kit.edu>
Date: Sat, 18 Jan 2025 14:27:06 +0100
Subject: [PATCH 1/3] add a second parameter to "view structure" to display
 constraints

---
 src/virtmat/language/constraints/amml.py      | 27 +++++++++++++++++++
 .../language/constraints/processors.py        |  2 ++
 src/virtmat/language/constraints/view.py      | 17 ++++++++++++
 src/virtmat/language/utilities/ase_viewers.py |  7 ++++-
 4 files changed, 52 insertions(+), 1 deletion(-)
 create mode 100644 src/virtmat/language/constraints/view.py

diff --git a/src/virtmat/language/constraints/amml.py b/src/virtmat/language/constraints/amml.py
index d0f96ba8..9bb89d57 100644
--- a/src/virtmat/language/constraints/amml.py
+++ b/src/virtmat/language/constraints/amml.py
@@ -1,7 +1,9 @@
 """checks / constraints for AMML objects"""
 from textx import get_children_of_type
+from virtmat.language.utilities.errors import StaticTypeError
 from virtmat.language.utilities.errors import raise_exception, StaticValueError
 from virtmat.language.utilities.textx import get_reference
+from virtmat.language.utilities.typemap import typemap
 from virtmat.language.utilities.ase_params import spec
 
 task_properties = {
@@ -44,3 +46,28 @@ def check_amml_property_processor(model, _):
             if not algo and calc_task and name not in task_properties[calc_task]:
                 msg = f'property \"{name}\" not available in task \"{calc_task}\"'
                 raise_exception(obj, StaticValueError, msg)
+
+
+def check_view_amml_structure_processor(model, _):
+    """check the parameters of view structure statements"""
+    for obj in get_children_of_type('View', model):
+        if obj.mode != 'structure':
+            continue
+        if len(obj.params) > 2:
+            msg = f'view structure has maximum 2 parameters but {len(obj.params)} given'
+            raise_exception(obj, StaticTypeError, msg)
+        if obj.params[0].type_:
+            if not issubclass(obj.params[0].type_, typemap['AMMLStructure']):
+                msg = (f'parameter must be type {typemap["AMMLStructure"].__name__}'
+                       f' but is type {obj.params[0].type_.__name__}')
+                raise_exception(obj.params[0], StaticTypeError, msg)
+        if len(obj.params) == 2:
+            if obj.params[1].type_:
+                if not issubclass(obj.params[1].type_, typemap['Tuple']):
+                    msg = 'parameter must be Tuple of constraints'
+                    raise_exception(obj.params[1], StaticTypeError, msg)
+                for type_ in obj.params[1].type_.datatype:
+                    if type_ and not issubclass(type_, typemap['AMMLConstraint']):
+                        msg = (f'parameter must be type {typemap["AMMLConstraint"].__name__}'
+                               f' but is type {type_.__name__}')
+                        raise_exception(obj.params[1], StaticTypeError, msg)
diff --git a/src/virtmat/language/constraints/processors.py b/src/virtmat/language/constraints/processors.py
index c5f5c85a..ae2931b2 100644
--- a/src/virtmat/language/constraints/processors.py
+++ b/src/virtmat/language/constraints/processors.py
@@ -20,6 +20,7 @@ from .functions import check_functions_processor
 from .imports import check_imports_processor
 from .units import check_units_processor
 from .parallel import check_parallelizable_processor
+from .view import check_view_processor
 from .amml import check_amml_property_processor
 from .chem import check_chem_reaction_processor
 
@@ -122,5 +123,6 @@ def add_constraints_processors(metamodel):
     metamodel.register_model_processor(check_parallelizable_processor)
     metamodel.register_model_processor(check_vary_processor)
     metamodel.register_model_processor(check_tag_processor)
+    metamodel.register_model_processor(check_view_processor)
     metamodel.register_model_processor(check_amml_property_processor)
     metamodel.register_model_processor(check_chem_reaction_processor)
diff --git a/src/virtmat/language/constraints/view.py b/src/virtmat/language/constraints/view.py
new file mode 100644
index 00000000..7844cb96
--- /dev/null
+++ b/src/virtmat/language/constraints/view.py
@@ -0,0 +1,17 @@
+"""apply constraints for all view statements"""
+from virtmat.language.utilities.errors import textxerror_wrap
+from .amml import check_view_amml_structure_processor
+
+
+@textxerror_wrap
+def check_view_processor(model, metamodel):
+    """apply constraints for all view statements"""
+    # check_view_lineplot_processor(model, metamodel)
+    # check_view_scatterplot_processor(model, metamodel)
+    check_view_amml_structure_processor(model, metamodel)
+    # check_view_amml_trajectory_processor(model, metamodel)
+    # check_view_amml_vibration_processor(model, metamodel)
+    # check_view_amml_neb_processor(model, metamodel)
+    # check_view_amml_bs_processor(model, metamodel)
+    # check_view_amml_eos_processor(model, metamodel)
+    # check_view_amml_waterfall_processor(model, metamodel)
diff --git a/src/virtmat/language/utilities/ase_viewers.py b/src/virtmat/language/utilities/ase_viewers.py
index f4d288b6..253bb9f1 100644
--- a/src/virtmat/language/utilities/ase_viewers.py
+++ b/src/virtmat/language/utilities/ase_viewers.py
@@ -24,7 +24,12 @@ def show_atoms(atoms, show=True):
 
 def display_amml_structure(obj, show=True):
     """display an atomic structure using ASE"""
-    show_atoms(obj.params[0].value.to_ase(), show=show)
+    atoms_lst = obj.params[0].value.to_ase()
+    if len(obj.params) == 2:
+        constraints = [c for cs in obj.params[1].value for c in cs.to_ase()]
+        for atoms_obj in atoms_lst:
+            atoms_obj.constraints = constraints
+    show_atoms(atoms_lst, show=show)
 
 
 def display_amml_trajectory(obj, show=True):
-- 
GitLab


From 95e0657eeeb2155d0f769db19dd101fada8b5b85 Mon Sep 17 00:00:00 2001
From: Ivan Kondov <ivan.kondov@kit.edu>
Date: Sat, 18 Jan 2025 16:52:12 +0100
Subject: [PATCH 2/3] add tests for view structure type checks

---
 tests/test_amml.py | 46 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 46 insertions(+)

diff --git a/tests/test_amml.py b/tests/test_amml.py
index f0aef064..27e97488 100644
--- a/tests/test_amml.py
+++ b/tests/test_amml.py
@@ -754,6 +754,52 @@ def test_view_amml_structure(meta_model, model_kwargs_no_display):
     assert meta_model.model_from_str(inp, **model_kwargs_no_display).value == ''
 
 
+def test_view_amml_structure_with_constraints(meta_model, model_kwargs_no_display):
+    """test view atomic structure with constraints"""
+    inp = ("h = Structure H ((atoms: ((symbols: 'H'), (x: 0.) [angstrom],"
+           " (y: 0.) [angstrom], (z: 0.) [angstrom])));"
+           "view structure (h, (FixedAtoms where (fixed: true),))")
+    assert meta_model.model_from_str(inp, **model_kwargs_no_display).value == ''
+
+
+def test_view_amml_structure_wrong_par_len(meta_model, model_kwargs_no_display):
+    """test view atomic structure with wrong number of parameters"""
+    inp = 'view structure (1, 2, 3)'
+    msg = 'view structure has maximum 2 parameters but 3 given'
+    with pytest.raises(TextXError, match=msg) as err:
+        meta_model.model_from_str(inp, **model_kwargs_no_display)
+    assert isinstance(err.value.__cause__, StaticTypeError)
+
+
+def test_view_amml_structure_wrong_first_par(meta_model, model_kwargs_no_display):
+    """test view atomic structure with wrong first parameter"""
+    inp = 'view structure (1, )'
+    msg = 'parameter must be type AMMLStructure but is type Quantity'
+    with pytest.raises(TextXError, match=msg) as err:
+        meta_model.model_from_str(inp, **model_kwargs_no_display)
+    assert isinstance(err.value.__cause__, StaticTypeError)
+
+
+def test_view_amml_structure_wrong_second_par(meta_model, model_kwargs_no_display):
+    """test view atomic structure with wrong type of second parameter"""
+    inp = ("h = Structure H ((atoms: ((symbols: 'H'), (x: 0.) [angstrom],"
+           " (y: 0.) [angstrom], (z: 0.) [angstrom]))); view structure (h, 1)")
+    msg = 'parameter must be Tuple of constraints'
+    with pytest.raises(TextXError, match=msg) as err:
+        meta_model.model_from_str(inp, **model_kwargs_no_display)
+    assert isinstance(err.value.__cause__, StaticTypeError)
+
+
+def test_view_amml_structure_wrong_second_par_type(meta_model, model_kwargs_no_display):
+    """test view atomic structure with a wrong type in second parameter"""
+    inp = ("h = Structure H ((atoms: ((symbols: 'H'), (x: 0.) [angstrom],"
+           " (y: 0.) [angstrom], (z: 0.) [angstrom]))); view structure (h, (1,))")
+    msg = 'parameter must be type Constraint but is type Quantity'
+    with pytest.raises(TextXError, match=msg) as err:
+        meta_model.model_from_str(inp, **model_kwargs_no_display)
+    assert isinstance(err.value.__cause__, StaticTypeError)
+
+
 def test_view_equation_of_state(meta_model, model_kwargs_no_display):
     """test view equation of state"""
     inp = ("atoms = ((symbols: 'Ag'), (x: 0.) [angstrom], (y: 0.) [angstrom],"
-- 
GitLab


From fa88433b78dbe34a49eb6da4da276af584d80510 Mon Sep 17 00:00:00 2001
From: Ivan Kondov <ivan.kondov@kit.edu>
Date: Sat, 18 Jan 2025 16:42:19 +0000
Subject: [PATCH 3/3] add the optional constraints to view structure docs

---
 docs/amml.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/docs/amml.md b/docs/amml.md
index 21864c05..9ea6a7f6 100644
--- a/docs/amml.md
+++ b/docs/amml.md
@@ -517,10 +517,10 @@ The [`view` statement](scl.md#the-view-statement) can be used to visualize and a
 
 ### Visualize / analyze atomic structures
 
-Parameters of type [AMML Structure](amml.md#structure) can be viewed using this simple syntax:
+Parameters of type [AMML Structure](amml.md#structure) with optional geometry constraints can be viewed using this syntax:
 
 ```
-view structure (struct)
+view structure (struct, [(constr1, [[constr2], ...])])
 ```
 
 ### Visualize a trajectory
-- 
GitLab