From a8fb852c537fb4f5e9a17034c87f0d0878a90522 Mon Sep 17 00:00:00 2001 From: LNavick Date: Wed, 1 Oct 2025 11:34:16 +0300 Subject: [PATCH 1/2] feature: flowcnec limit consistency check --- rao/crac/builder.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/rao/crac/builder.py b/rao/crac/builder.py index dcc3aa2..e33bdac 100644 --- a/rao/crac/builder.py +++ b/rao/crac/builder.py @@ -34,6 +34,30 @@ def crac(self): def crac_pprint(self): return print(json.dumps(self.crac, indent=2)) + def perform_cnec_consistency_check(self): + + # Find the flowCnec thresholds + flow_cnecs = list(getattr(self._crac, "flowCnecs", [])) + kept = [] + + for cnec in flow_cnecs: + cnec_name = getattr(cnec, "name", None) + + thresholds = getattr(cnec, "thresholds", []) or [] + if not isinstance(thresholds, list): + thresholds = [thresholds] + + # Flag FlowCNEC as removed if limits min=0, max=0 + removed = any((getattr(th, "min", None) == 0) and (getattr(th, "max", None) == 0) for th in thresholds) + + if removed: + logger.warning(f"CNEC {cnec_name} removed from the CRAC file due to missing limits") + else: + kept.append(cnec) + + # Keep only consistent flowCnecs + setattr(self._crac, "flowCnecs", kept) + def get_limits(self): if self.network is None: @@ -385,6 +409,7 @@ def build_crac(self, contingency_ids: list | None = None): self.process_cnecs() self.process_remedial_actions() self.update_limits_from_network() + self.perform_cnec_consistency_check() return self.crac From 7c89f52b33e99504a4aded4096163fdc4dc7300c Mon Sep 17 00:00:00 2001 From: LNavick Date: Sat, 11 Apr 2026 15:27:13 +0300 Subject: [PATCH 2/2] feat: correct Nan limit handling for CNEC consistency check --- rao/crac/builder.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/rao/crac/builder.py b/rao/crac/builder.py index e33bdac..d2aff03 100644 --- a/rao/crac/builder.py +++ b/rao/crac/builder.py @@ -40,6 +40,18 @@ def perform_cnec_consistency_check(self): flow_cnecs = list(getattr(self._crac, "flowCnecs", [])) kept = [] + def _is_nan(value): + return isinstance(value, float) and math.isnan(value) + + def _is_invalid_threshold(threshold): + min_val = getattr(threshold, "min", None) + max_val = getattr(threshold, "max", None) + + min_missing = (min_val == 0) or _is_nan(min_val) + max_missing = (max_val == 0) or _is_nan(max_val) + + return min_missing and max_missing + for cnec in flow_cnecs: cnec_name = getattr(cnec, "name", None) @@ -47,15 +59,13 @@ def perform_cnec_consistency_check(self): if not isinstance(thresholds, list): thresholds = [thresholds] - # Flag FlowCNEC as removed if limits min=0, max=0 - removed = any((getattr(th, "min", None) == 0) and (getattr(th, "max", None) == 0) for th in thresholds) + removed = any(_is_invalid_threshold(th) for th in thresholds) if removed: - logger.warning(f"CNEC {cnec_name} removed from the CRAC file due to missing limits") + logger.warning(f"CNEC {cnec_name} removed from CRAC file due to missing limits") else: kept.append(cnec) - # Keep only consistent flowCnecs setattr(self._crac, "flowCnecs", kept) def get_limits(self):