From 7353f83f13fb232efd079ce060367dc7bbd86cfa Mon Sep 17 00:00:00 2001 From: icedoom888 Date: Tue, 17 Dec 2024 19:32:21 +0100 Subject: [PATCH 01/15] Implemented new attribute --- src/anemoi/graphs/edges/attributes.py | 71 +++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/src/anemoi/graphs/edges/attributes.py b/src/anemoi/graphs/edges/attributes.py index 6be801b..3aca8a8 100644 --- a/src/anemoi/graphs/edges/attributes.py +++ b/src/anemoi/graphs/edges/attributes.py @@ -155,3 +155,74 @@ def post_process(self, values: np.ndarray) -> torch.Tensor: values = 1 - values return values + + +class BooleanBaseEdgeAttribute: + """Base class for boolean edge attributes.""" + + def __init__(self) -> None: + pass + + @abstractmethod + def get_raw_values(self, graph: HeteroData, source_name: str, target_name: str, *args, **kwargs) -> np.ndarray: ... + + def post_process(self, values: np.ndarray) -> torch.Tensor: + """Post-process the values.""" + return torch.tensor(values, dtype=torch.bool) + + def compute(self, graph: HeteroData, edges_name: tuple[str, str, str], *args, **kwargs) -> torch.Tensor: + """Compute the edge attributes.""" + source_name, _, target_name = edges_name + assert ( + source_name in graph.node_types + ), f"Node \"{source_name}\" not found in graph. Optional nodes are {', '.join(graph.node_types)}." + assert ( + target_name in graph.node_types + ), f"Node \"{target_name}\" not found in graph. Optional nodes are {', '.join(graph.node_types)}." + + values = self.get_raw_values(graph, source_name, target_name, *args, **kwargs) + return self.post_process(values) + + +class AttributeFromNode(BooleanBaseEdgeAttribute): + """ + Copy an attribute of either the source or destination node to the edge. + Accesses origin/target node attribute and propagates it to the edge. + Used for example to identify if an encoder edge originates from a LAM or global node. + + Attributes + ---------- + node_attr_name : str + Name of the node attribute to propagate. + + node_type : str + Pick the node to copy from. Options: "src, dst" + + Methods + ------- + get_raw_values(graph, source_name, target_name) + Computes the edge attribute from the source or destination node attribute. + """ + + def __init__(self, node_attr_name: str, node_type: str) -> None: + self.node_attr_name = node_attr_name + assert node_type in ["src", "dst"] + self.node_type = node_type + + def get_raw_values(self, graph: HeteroData, source_name: str, target_name: str) -> np.ndarray: + + edge_index = graph[(source_name, "to", target_name)].edge_index + + if self.node_type == "src": + name_to_copy = source_name + idx = 0 + + else: + name_to_copy = target_name + idx = 1 + + assert hasattr(graph[name_to_copy], self.node_attr_name) + + val = getattr(graph[name_to_copy], self.node_attr_name).numpy()[edge_index[idx]] + + return val From fa5ce52e391b62df726cc9b23dd022d215701404 Mon Sep 17 00:00:00 2001 From: icedoom888 Date: Tue, 17 Dec 2024 19:40:53 +0100 Subject: [PATCH 02/15] changelog update --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f61f7cf..362053f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,8 @@ Keep it human-readable, your future self will thank you! - feat: Support for multiple edge builders between two sets of nodes (#70) - feat: Support for providing lon/lat coordinates from a text file (loaded with numpy loadtxt method) to build the graph `TextNodes` (#93) - feat: Build 2D graphs with `Voronoi` in case `SphericalVoronoi` does not work well/is an overkill (LAM). Set `flat=true` in the nodes attributes to compute area weight using Voronoi with a qhull options preventing the empty region creation (#93) +- feat: Add `AttributeFromNode` edge attribute to copy attribute from source or destination node. Set `node_attr_name` and `node_type : src | dst` in the config to specify which attribute to copy from the source | destination node (#94) + # Changed From 1bc01135a77f483ba3de9d0176e711ecd41d9a78 Mon Sep 17 00:00:00 2001 From: icedoom888 Date: Tue, 17 Dec 2024 19:41:07 +0100 Subject: [PATCH 03/15] changelog update --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 362053f..4420e8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,7 +24,7 @@ Keep it human-readable, your future self will thank you! - feat: Support for multiple edge builders between two sets of nodes (#70) - feat: Support for providing lon/lat coordinates from a text file (loaded with numpy loadtxt method) to build the graph `TextNodes` (#93) - feat: Build 2D graphs with `Voronoi` in case `SphericalVoronoi` does not work well/is an overkill (LAM). Set `flat=true` in the nodes attributes to compute area weight using Voronoi with a qhull options preventing the empty region creation (#93) -- feat: Add `AttributeFromNode` edge attribute to copy attribute from source or destination node. Set `node_attr_name` and `node_type : src | dst` in the config to specify which attribute to copy from the source | destination node (#94) +- feat: Add `AttributeFromNode` edge attribute to copy attribute from source or destination node. Set `node_attr_name` and `node_type : src | dst` in the config to specify which attribute to copy from the source | destination node (#94) # Changed From 919d0ade81235b67c70fd667a66da689bf85bc8e Mon Sep 17 00:00:00 2001 From: icedoom888 Date: Wed, 18 Dec 2024 16:05:31 +0100 Subject: [PATCH 04/15] Refactored following review --- CHANGELOG.md | 3 +- src/anemoi/graphs/edges/attributes.py | 88 ++++++++++++++------------- 2 files changed, 47 insertions(+), 44 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4420e8c..33c692b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,8 +24,7 @@ Keep it human-readable, your future self will thank you! - feat: Support for multiple edge builders between two sets of nodes (#70) - feat: Support for providing lon/lat coordinates from a text file (loaded with numpy loadtxt method) to build the graph `TextNodes` (#93) - feat: Build 2D graphs with `Voronoi` in case `SphericalVoronoi` does not work well/is an overkill (LAM). Set `flat=true` in the nodes attributes to compute area weight using Voronoi with a qhull options preventing the empty region creation (#93) -- feat: Add `AttributeFromNode` edge attribute to copy attribute from source or destination node. Set `node_attr_name` and `node_type : src | dst` in the config to specify which attribute to copy from the source | destination node (#94) - +- feat: Add `AttributeFromSourceNode` and `AttributeFromTargetNode` edge attribute to copy attribute from source or target node. Set `node_attr_name` in the config to specify which attribute to copy from the source | target node (#94) # Changed diff --git a/src/anemoi/graphs/edges/attributes.py b/src/anemoi/graphs/edges/attributes.py index 3aca8a8..3d8122e 100644 --- a/src/anemoi/graphs/edges/attributes.py +++ b/src/anemoi/graphs/edges/attributes.py @@ -24,8 +24,9 @@ class BaseEdgeAttribute(ABC, NormaliserMixin): """Base class for edge attributes.""" - def __init__(self, norm: str | None = None) -> None: + def __init__(self, norm: str | None = None, dtype: str = "float32") -> None: self.norm = norm + self.dtype = dtype @abstractmethod def get_raw_values(self, graph: HeteroData, source_name: str, target_name: str, *args, **kwargs) -> np.ndarray: ... @@ -35,9 +36,9 @@ def post_process(self, values: np.ndarray) -> torch.Tensor: if values.ndim == 1: values = values[:, np.newaxis] - normed_values = self.normalise(values) + norm_values = self.normalise(values) - return torch.tensor(normed_values, dtype=torch.float32) + return torch.tensor(norm_values.astype(self.dtype)) def compute(self, graph: HeteroData, edges_name: tuple[str, str, str], *args, **kwargs) -> torch.Tensor: """Compute the edge attributes.""" @@ -157,36 +158,18 @@ def post_process(self, values: np.ndarray) -> torch.Tensor: return values -class BooleanBaseEdgeAttribute: +class BooleanBaseEdgeAttribute(BaseEdgeAttribute): """Base class for boolean edge attributes.""" def __init__(self) -> None: - pass - - @abstractmethod - def get_raw_values(self, graph: HeteroData, source_name: str, target_name: str, *args, **kwargs) -> np.ndarray: ... - - def post_process(self, values: np.ndarray) -> torch.Tensor: - """Post-process the values.""" - return torch.tensor(values, dtype=torch.bool) - - def compute(self, graph: HeteroData, edges_name: tuple[str, str, str], *args, **kwargs) -> torch.Tensor: - """Compute the edge attributes.""" - source_name, _, target_name = edges_name - assert ( - source_name in graph.node_types - ), f"Node \"{source_name}\" not found in graph. Optional nodes are {', '.join(graph.node_types)}." - assert ( - target_name in graph.node_types - ), f"Node \"{target_name}\" not found in graph. Optional nodes are {', '.join(graph.node_types)}." - - values = self.get_raw_values(graph, source_name, target_name, *args, **kwargs) - return self.post_process(values) + super().__init__(norm=None, dtype="bool") class AttributeFromNode(BooleanBaseEdgeAttribute): """ - Copy an attribute of either the source or destination node to the edge. + Base class for Attribute from Node. + + Copy an attribute of either the source or target node to the edge. Accesses origin/target node attribute and propagates it to the edge. Used for example to identify if an encoder edge originates from a LAM or global node. @@ -195,34 +178,55 @@ class AttributeFromNode(BooleanBaseEdgeAttribute): node_attr_name : str Name of the node attribute to propagate. - node_type : str - Pick the node to copy from. Options: "src, dst" - Methods ------- + get_node_name(source_name, target_name) + Return the name of the node to copy. + get_raw_values(graph, source_name, target_name) - Computes the edge attribute from the source or destination node attribute. + Computes the edge attribute from the source or target node attribute. + """ - def __init__(self, node_attr_name: str, node_type: str) -> None: + def __init__(self, node_attr_name: str) -> None: + super().__init__() self.node_attr_name = node_attr_name - assert node_type in ["src", "dst"] - self.node_type = node_type + self.idx = None + + @abstractmethod + def get_node_name(self, source_name: str, target_name: str): ... def get_raw_values(self, graph: HeteroData, source_name: str, target_name: str) -> np.ndarray: + node_name = self.get_node_name(source_name, target_name) + edge_index = graph[(source_name, "to", target_name)].edge_index + assert hasattr(graph[node_name], self.node_attr_name) + val = getattr(graph[node_name], self.node_attr_name).numpy()[edge_index[self.idx]] + return val - if self.node_type == "src": - name_to_copy = source_name - idx = 0 - else: - name_to_copy = target_name - idx = 1 +class AttributeFromSourceNode(AttributeFromNode): + """ + Copy an attribute of the source node to the edge. + """ - assert hasattr(graph[name_to_copy], self.node_attr_name) + def __init__(self, node_attr_name: str) -> None: + super().__init__(node_attr_name) + self.idx = 0 - val = getattr(graph[name_to_copy], self.node_attr_name).numpy()[edge_index[idx]] + def get_node_name(self, source_name: str, target_name: str): + return source_name - return val + +class AttributeFromTargetNode(AttributeFromNode): + """ + Copy an attribute of the target node to the edge. + """ + + def __init__(self, node_attr_name: str) -> None: + super().__init__(node_attr_name) + self.idx = 1 + + def get_node_name(self, source_name: str, target_name: str): + return target_name From 40394e479ded9c40116554acfff37e52c5537c31 Mon Sep 17 00:00:00 2001 From: Icedoom <9220778+icedoom888@users.noreply.github.com> Date: Wed, 18 Dec 2024 18:50:57 +0100 Subject: [PATCH 05/15] Update src/anemoi/graphs/edges/attributes.py Co-authored-by: Mario Santa Cruz <48736305+JPXKQX@users.noreply.github.com> --- src/anemoi/graphs/edges/attributes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/anemoi/graphs/edges/attributes.py b/src/anemoi/graphs/edges/attributes.py index 3d8122e..3c1a21e 100644 --- a/src/anemoi/graphs/edges/attributes.py +++ b/src/anemoi/graphs/edges/attributes.py @@ -202,7 +202,7 @@ def get_raw_values(self, graph: HeteroData, source_name: str, target_name: str) edge_index = graph[(source_name, "to", target_name)].edge_index assert hasattr(graph[node_name], self.node_attr_name) - val = getattr(graph[node_name], self.node_attr_name).numpy()[edge_index[self.idx]] + return graph[node_name][self.node_attr_name].numpy()[edge_index[self.idx]] return val From 77b233588ffba4f2318da0613cea518b3e220580 Mon Sep 17 00:00:00 2001 From: Icedoom <9220778+icedoom888@users.noreply.github.com> Date: Wed, 18 Dec 2024 18:51:06 +0100 Subject: [PATCH 06/15] Update src/anemoi/graphs/edges/attributes.py Co-authored-by: Mario Santa Cruz <48736305+JPXKQX@users.noreply.github.com> --- src/anemoi/graphs/edges/attributes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/anemoi/graphs/edges/attributes.py b/src/anemoi/graphs/edges/attributes.py index 3c1a21e..6c9d16c 100644 --- a/src/anemoi/graphs/edges/attributes.py +++ b/src/anemoi/graphs/edges/attributes.py @@ -170,7 +170,7 @@ class AttributeFromNode(BooleanBaseEdgeAttribute): Base class for Attribute from Node. Copy an attribute of either the source or target node to the edge. - Accesses origin/target node attribute and propagates it to the edge. + Accesses source/target node attribute and propagates it to the edge. Used for example to identify if an encoder edge originates from a LAM or global node. Attributes From a58dae6b864e693166e0833d6a398bd3e7937253 Mon Sep 17 00:00:00 2001 From: Icedoom <9220778+icedoom888@users.noreply.github.com> Date: Wed, 18 Dec 2024 18:51:12 +0100 Subject: [PATCH 07/15] Update src/anemoi/graphs/edges/attributes.py Co-authored-by: Mario Santa Cruz <48736305+JPXKQX@users.noreply.github.com> --- src/anemoi/graphs/edges/attributes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/anemoi/graphs/edges/attributes.py b/src/anemoi/graphs/edges/attributes.py index 6c9d16c..cf71ccd 100644 --- a/src/anemoi/graphs/edges/attributes.py +++ b/src/anemoi/graphs/edges/attributes.py @@ -158,7 +158,7 @@ def post_process(self, values: np.ndarray) -> torch.Tensor: return values -class BooleanBaseEdgeAttribute(BaseEdgeAttribute): +class BooleanBaseEdgeAttribute(ABC, BaseEdgeAttribute): """Base class for boolean edge attributes.""" def __init__(self) -> None: From ab2f6a32e1dd9dfac804632bb2dc309d4c99d52f Mon Sep 17 00:00:00 2001 From: Icedoom <9220778+icedoom888@users.noreply.github.com> Date: Wed, 18 Dec 2024 18:51:20 +0100 Subject: [PATCH 08/15] Update src/anemoi/graphs/edges/attributes.py Co-authored-by: Mario Santa Cruz <48736305+JPXKQX@users.noreply.github.com> --- src/anemoi/graphs/edges/attributes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/anemoi/graphs/edges/attributes.py b/src/anemoi/graphs/edges/attributes.py index cf71ccd..7be8813 100644 --- a/src/anemoi/graphs/edges/attributes.py +++ b/src/anemoi/graphs/edges/attributes.py @@ -165,7 +165,7 @@ def __init__(self) -> None: super().__init__(norm=None, dtype="bool") -class AttributeFromNode(BooleanBaseEdgeAttribute): +class AttributeFromNode(ABC, BooleanBaseEdgeAttribute): """ Base class for Attribute from Node. From 11387b7d67e425fb83df6e2522eba11344b3541f Mon Sep 17 00:00:00 2001 From: icedoom888 Date: Wed, 18 Dec 2024 18:54:17 +0100 Subject: [PATCH 09/15] Refactor after review --- src/anemoi/graphs/edges/attributes.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/anemoi/graphs/edges/attributes.py b/src/anemoi/graphs/edges/attributes.py index 7be8813..9555bd1 100644 --- a/src/anemoi/graphs/edges/attributes.py +++ b/src/anemoi/graphs/edges/attributes.py @@ -203,8 +203,6 @@ def get_raw_values(self, graph: HeteroData, source_name: str, target_name: str) edge_index = graph[(source_name, "to", target_name)].edge_index assert hasattr(graph[node_name], self.node_attr_name) return graph[node_name][self.node_attr_name].numpy()[edge_index[self.idx]] - return val - class AttributeFromSourceNode(AttributeFromNode): """ From 8602b15032452d5eb5b1cb36bdaaa380a89eb3dc Mon Sep 17 00:00:00 2001 From: icedoom888 Date: Wed, 18 Dec 2024 19:05:17 +0100 Subject: [PATCH 10/15] Docstring done --- docs/graphs/edge_attributes.rst | 46 ++++++++++++++++++++++++++- src/anemoi/graphs/edges/attributes.py | 1 + 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/docs/graphs/edge_attributes.rst b/docs/graphs/edge_attributes.rst index 1c99149..3e862dc 100644 --- a/docs/graphs/edge_attributes.rst +++ b/docs/graphs/edge_attributes.rst @@ -4,7 +4,7 @@ Edges - Attributes #################### -There are 2 main edge attributes implemented in the `anemoi-graphs` +There are few edge attributes implemented in the `anemoi-graphs` package: ************* @@ -44,3 +44,47 @@ latitude and longitude coordinates of the source and target nodes. attributes: edge_length: _target_: anemoi.graphs.edges.attributes.EdgeDirection + +********************* + Attribute from Node +********************* + +Attributes can also be copied from nodes to edges. This is done using +the `AttributeFromNode` base class, with specialized versions for source +and target nodes. + +************* + From Source +************* + +This attribute copies a specific property of the source node to the +edge. + +.. code:: yaml + + edges: + - source_name: ... + target_name: ... + edge_builders: ... + attributes: + attribute_name: + _target_: anemoi.graphs.edges.attributes.AttributeFromSourceNode + node_attr_name: "attribute_name" + +************* + From Target +************* + +This attribute copies a specific property of the target node to the +edge. + +.. code:: yaml + + edges: + - source_name: ... + target_name: ... + edge_builders: ... + attributes: + attribute_name: + _target_: anemoi.graphs.edges.attributes.AttributeFromTargetNode + node_attr_name: "attribute_name" diff --git a/src/anemoi/graphs/edges/attributes.py b/src/anemoi/graphs/edges/attributes.py index 9555bd1..73079e0 100644 --- a/src/anemoi/graphs/edges/attributes.py +++ b/src/anemoi/graphs/edges/attributes.py @@ -204,6 +204,7 @@ def get_raw_values(self, graph: HeteroData, source_name: str, target_name: str) assert hasattr(graph[node_name], self.node_attr_name) return graph[node_name][self.node_attr_name].numpy()[edge_index[self.idx]] + class AttributeFromSourceNode(AttributeFromNode): """ Copy an attribute of the source node to the edge. From 18e9a75e8f01b92837a74c73592727c41921225b Mon Sep 17 00:00:00 2001 From: icedoom888 Date: Wed, 18 Dec 2024 19:10:23 +0100 Subject: [PATCH 11/15] Docstring done --- docs/graphs/edge_attributes.rst | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/docs/graphs/edge_attributes.rst b/docs/graphs/edge_attributes.rst index 3e862dc..90b4e37 100644 --- a/docs/graphs/edge_attributes.rst +++ b/docs/graphs/edge_attributes.rst @@ -67,10 +67,25 @@ edge. target_name: ... edge_builders: ... attributes: - attribute_name: + comes_from_source_node: _target_: anemoi.graphs.edges.attributes.AttributeFromSourceNode node_attr_name: "attribute_name" + +Example usage for copying the cutout mask from nodes to edges in the encoder: + +.. code:: yaml + edges: + # Encoder + - source_name: data + target_name: hidden + edge_builders: ... + attributes: + cutout: + _target_: anemoi.graphs.edges.attributes.AttributeFromSourceNode + node_attr_name: cutout + + ************* From Target ************* @@ -85,6 +100,19 @@ edge. target_name: ... edge_builders: ... attributes: - attribute_name: + comes_from_target_node: _target_: anemoi.graphs.edges.attributes.AttributeFromTargetNode node_attr_name: "attribute_name" + +Example usage for copying the coutout mask from nodes to edges in the decoder: + +.. code:: yaml + edges: + # Decoder + - source_name: hidden + target_name: data + edge_builders: ... + attributes: + cutout: + _target_: anemoi.graphs.edges.attributes.AttributeFromTargetNode + node_attr_name: cutout From 89af39ae382f3bbbd8359589fedf47135e5dee3d Mon Sep 17 00:00:00 2001 From: icedoom888 Date: Wed, 18 Dec 2024 19:12:08 +0100 Subject: [PATCH 12/15] Docstring done --- docs/graphs/edge_attributes.rst | 44 +++++++++++++++++---------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/docs/graphs/edge_attributes.rst b/docs/graphs/edge_attributes.rst index 90b4e37..27c8457 100644 --- a/docs/graphs/edge_attributes.rst +++ b/docs/graphs/edge_attributes.rst @@ -71,20 +71,20 @@ edge. _target_: anemoi.graphs.edges.attributes.AttributeFromSourceNode node_attr_name: "attribute_name" - -Example usage for copying the cutout mask from nodes to edges in the encoder: +Example usage for copying the cutout mask from nodes to edges in the +encoder: .. code:: yaml - edges: - # Encoder - - source_name: data - target_name: hidden - edge_builders: ... - attributes: - cutout: - _target_: anemoi.graphs.edges.attributes.AttributeFromSourceNode - node_attr_name: cutout + edges: + # Encoder + - source_name: data + target_name: hidden + edge_builders: ... + attributes: + cutout: + _target_: anemoi.graphs.edges.attributes.AttributeFromSourceNode + node_attr_name: cutout ************* From Target @@ -104,15 +104,17 @@ edge. _target_: anemoi.graphs.edges.attributes.AttributeFromTargetNode node_attr_name: "attribute_name" -Example usage for copying the coutout mask from nodes to edges in the decoder: +Example usage for copying the coutout mask from nodes to edges in the +decoder: .. code:: yaml - edges: - # Decoder - - source_name: hidden - target_name: data - edge_builders: ... - attributes: - cutout: - _target_: anemoi.graphs.edges.attributes.AttributeFromTargetNode - node_attr_name: cutout + + edges: + # Decoder + - source_name: hidden + target_name: data + edge_builders: ... + attributes: + cutout: + _target_: anemoi.graphs.edges.attributes.AttributeFromTargetNode + node_attr_name: cutout From d3611d36c17040439929db2083a2f466cb5ba6da Mon Sep 17 00:00:00 2001 From: icedoom888 Date: Wed, 18 Dec 2024 19:18:18 +0100 Subject: [PATCH 13/15] Fixed ABC issue --- src/anemoi/graphs/edges/attributes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/anemoi/graphs/edges/attributes.py b/src/anemoi/graphs/edges/attributes.py index 73079e0..b76d6d1 100644 --- a/src/anemoi/graphs/edges/attributes.py +++ b/src/anemoi/graphs/edges/attributes.py @@ -158,14 +158,14 @@ def post_process(self, values: np.ndarray) -> torch.Tensor: return values -class BooleanBaseEdgeAttribute(ABC, BaseEdgeAttribute): +class BooleanBaseEdgeAttribute(BaseEdgeAttribute, ABC): """Base class for boolean edge attributes.""" def __init__(self) -> None: super().__init__(norm=None, dtype="bool") -class AttributeFromNode(ABC, BooleanBaseEdgeAttribute): +class AttributeFromNode(BooleanBaseEdgeAttribute, ABC): """ Base class for Attribute from Node. From 5cc3ca8f88f69cfd49073efb15274abc79f805e7 Mon Sep 17 00:00:00 2001 From: icedoom888 Date: Thu, 19 Dec 2024 18:07:23 +0100 Subject: [PATCH 14/15] addressed changes in docs and exception error --- docs/graphs/edge_attributes.rst | 48 ++++++--------------------- src/anemoi/graphs/edges/attributes.py | 9 +++-- 2 files changed, 17 insertions(+), 40 deletions(-) diff --git a/docs/graphs/edge_attributes.rst b/docs/graphs/edge_attributes.rst index 27c8457..d2f4b47 100644 --- a/docs/graphs/edge_attributes.rst +++ b/docs/graphs/edge_attributes.rst @@ -53,26 +53,12 @@ Attributes can also be copied from nodes to edges. This is done using the `AttributeFromNode` base class, with specialized versions for source and target nodes. -************* - From Source -************* +From Source +=========== This attribute copies a specific property of the source node to the -edge. - -.. code:: yaml - - edges: - - source_name: ... - target_name: ... - edge_builders: ... - attributes: - comes_from_source_node: - _target_: anemoi.graphs.edges.attributes.AttributeFromSourceNode - node_attr_name: "attribute_name" - -Example usage for copying the cutout mask from nodes to edges in the -encoder: +edge. Example usage for copying the cutout mask from nodes to edges in +the encoder: .. code:: yaml @@ -82,30 +68,16 @@ encoder: target_name: hidden edge_builders: ... attributes: - cutout: + cutout: # Assigned name to the edge attribute, can be different than node_attr_name _target_: anemoi.graphs.edges.attributes.AttributeFromSourceNode node_attr_name: cutout -************* - From Target -************* +From Target +=========== This attribute copies a specific property of the target node to the -edge. - -.. code:: yaml - - edges: - - source_name: ... - target_name: ... - edge_builders: ... - attributes: - comes_from_target_node: - _target_: anemoi.graphs.edges.attributes.AttributeFromTargetNode - node_attr_name: "attribute_name" - -Example usage for copying the coutout mask from nodes to edges in the -decoder: +edge. Example usage for copying the coutout mask from nodes to edges in +the decoder: .. code:: yaml @@ -115,6 +87,6 @@ decoder: target_name: data edge_builders: ... attributes: - cutout: + cutout: # Assigned name to the edge attribute, can be different than node_attr_name _target_: anemoi.graphs.edges.attributes.AttributeFromTargetNode node_attr_name: cutout diff --git a/src/anemoi/graphs/edges/attributes.py b/src/anemoi/graphs/edges/attributes.py index b76d6d1..9d43ac0 100644 --- a/src/anemoi/graphs/edges/attributes.py +++ b/src/anemoi/graphs/edges/attributes.py @@ -201,8 +201,13 @@ def get_raw_values(self, graph: HeteroData, source_name: str, target_name: str) node_name = self.get_node_name(source_name, target_name) edge_index = graph[(source_name, "to", target_name)].edge_index - assert hasattr(graph[node_name], self.node_attr_name) - return graph[node_name][self.node_attr_name].numpy()[edge_index[self.idx]] + try: + return graph[node_name][self.node_attr_name].numpy()[edge_index[self.idx]] + + except AttributeError: + raise AttributeError( + f"{self.__class__.__name__} failed because the attribute '{self.node_attr_name}' is not defined for the nodes." + ) class AttributeFromSourceNode(AttributeFromNode): From 7af61c24a9a381fcef2a343b55e28c587123a482 Mon Sep 17 00:00:00 2001 From: icedoom888 Date: Thu, 19 Dec 2024 18:09:00 +0100 Subject: [PATCH 15/15] Changed changelog --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33c692b..7e1f964 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ Keep it human-readable, your future self will thank you! ## [Unreleased](https://github.com/ecmwf/anemoi-graphs/compare/0.4.1...HEAD) +### Added + +- feat: Add `AttributeFromSourceNode` and `AttributeFromTargetNode` edge attribute to copy attribute from source or target node. Set `node_attr_name` in the config to specify which attribute to copy from the source | target node (#94) + # Changed - fix: faster edge builder for tri icosahedron. (#92) @@ -24,7 +28,6 @@ Keep it human-readable, your future self will thank you! - feat: Support for multiple edge builders between two sets of nodes (#70) - feat: Support for providing lon/lat coordinates from a text file (loaded with numpy loadtxt method) to build the graph `TextNodes` (#93) - feat: Build 2D graphs with `Voronoi` in case `SphericalVoronoi` does not work well/is an overkill (LAM). Set `flat=true` in the nodes attributes to compute area weight using Voronoi with a qhull options preventing the empty region creation (#93) -- feat: Add `AttributeFromSourceNode` and `AttributeFromTargetNode` edge attribute to copy attribute from source or target node. Set `node_attr_name` in the config to specify which attribute to copy from the source | target node (#94) # Changed