From e17a662c22dcc682b835bfeb219e2df2055fc1b4 Mon Sep 17 00:00:00 2001 From: Jun Chen Date: Wed, 11 Oct 2023 17:42:37 +0200 Subject: [PATCH 01/11] To test the push action --- micro_manager/adaptivity/adaptivity.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/micro_manager/adaptivity/adaptivity.py b/micro_manager/adaptivity/adaptivity.py index 44e2e2ee..0d072de6 100644 --- a/micro_manager/adaptivity/adaptivity.py +++ b/micro_manager/adaptivity/adaptivity.py @@ -84,7 +84,7 @@ def _update_active_sims( _is_sim_active : numpy array Updated 1D array having state (active or inactive) of each micro simulation """ - self._coarse_tol = self._coarse_const * self._refine_const * np.amax(similarity_dists) + self._coarse_tol = self._coarse_const * self._refine_const * np.amax(similarity_dists)# adapt the constant factor here _is_sim_active = np.copy(is_sim_active) # Input is_sim_active is not longer used after this point From 69a058ef948743f98820289fc5da4d482c9c48e9 Mon Sep 17 00:00:00 2001 From: Jun Chen Date: Tue, 14 Nov 2023 15:39:54 +0100 Subject: [PATCH 02/11] Add funtion with adaptive coarsening/refining constant --- examples/micro-manager-adaptivity-config.json | 3 +- micro_manager/adaptivity/adaptivity.py | 95 ++++++++++++++++++- micro_manager/adaptivity/global_adaptivity.py | 2 + micro_manager/adaptivity/local_adaptivity.py | 2 + micro_manager/config.py | 38 +++++++- 5 files changed, 135 insertions(+), 5 deletions(-) diff --git a/examples/micro-manager-adaptivity-config.json b/examples/micro-manager-adaptivity-config.json index 76a7fd78..ff06d9f2 100644 --- a/examples/micro-manager-adaptivity-config.json +++ b/examples/micro-manager-adaptivity-config.json @@ -14,7 +14,8 @@ "history_param": 0.5, "coarsening_constant": 0.3, "refining_constant": 0.4, - "every_implicit_iteration": "True" + "every_implicit_iteration": "True", + "adaptive_coarsening_constant": "True" } }, "diagnostics": { diff --git a/micro_manager/adaptivity/adaptivity.py b/micro_manager/adaptivity/adaptivity.py index 0d072de6..00ddd6ca 100644 --- a/micro_manager/adaptivity/adaptivity.py +++ b/micro_manager/adaptivity/adaptivity.py @@ -2,9 +2,12 @@ Functionality for adaptive initialization and control of micro simulations """ import sys +import os import numpy as np from math import exp from typing import Callable +import re +import xml.etree.ElementTree as ET class AdaptivityCalculator: @@ -18,11 +21,16 @@ def __init__(self, configurator, logger) -> None: Object which has getter functions to get parameters defined in the configuration file. logger : Logger defined from the standard package logging """ - self._refine_const = configurator.get_adaptivity_refining_const() - self._coarse_const = configurator.get_adaptivity_coarsening_const() + self._refine_const_input = configurator.get_adaptivity_refining_const() + self._refine_const = self._refine_const_input + self._coarse_const_input = configurator.get_adaptivity_coarsening_const() + self._coarse_const = self._coarse_const_input + self._adaptive_coarse_const = configurator.get_adaptivity_for_coarsening_const() + self._adaptive_refine_const = configurator.get_adaptivity_for_refining_const() self._hist_param = configurator.get_adaptivity_hist_param() self._adaptivity_data_names = configurator.get_data_for_adaptivity() self._adaptivity_type = configurator.get_adaptivity_type() + self._config_file_name = configurator.get_config_file_name() self._logger = logger @@ -64,6 +72,82 @@ def _get_similarity_dists(self, dt: float, similarity_dists: np.ndarray, data: d return exp(-self._hist_param * dt) * _similarity_dists + dt * data_diff + def _get_adaptive_similarity_const(self, similarity_const: float) -> float: + """ + Get adapted coarsening/refining constant based on limit values in preCICE configuration file and convergence measurements in preCICE + + Returns + ------- + adaptive_similartity_const : float + """ + # Read the XML file as text + with open(self._config_file_name, 'r') as xml_file: + xml_data = xml_file.read() + + unique_names = ["absolute-convergence-measure","relative-convergence-measure","residual-relative-convergence-measure"] + + # Initialize lists to store the found attributes + data_values = [] + limit_values = [] + + for unique_name in unique_names: + patteren = f'<{unique_name} limit="([^"]+)" data="([^"]+)" mesh="([^"]+)"' + matches = re.finditer(patteren, xml_data) + for match in matches: + data_values.append(match.group(2)) + limit_values.append(match.group(1)) + + # Check if any matches were found + if data_values and limit_values: + for i, (data_value, limit_value) in enumerate(zip(data_values, limit_values), start=1): + print(f"Match {i}:") + print(f"Data: {data_value}") + print(f"Limit: {limit_value}") + else: + print(f"No attributes found for unique name '{unique_name}'") + + # read convergence value from precice-Mysolver-convergence.log file + # Initialize lists to store the extracted values + convergence_values = [] + + file_path = None + file_name_suffix = "-convergence.log" + + # Search for the file in the current directory and its subdirectories + for root, _, files in os.walk(os.getcwd()): + for file_name in files: + if file_name.endswith(file_name_suffix): + file_path = os.path.join(root, file_name) + break + + if file_path: + with open(file_path, "r") as file: + lines = file.readlines() + if len(lines) < 2: + print("File does not contain enough lines.") + adaptive_similartity_const = similarity_const + else: + # Read the header line and last line of the file + header_line = lines[0].strip().split( ) # Assuming columns are tab-separated + last_line = lines[-1].strip().split( ) + for data in data_values: + for element in header_line: + if data in element: + index = header_line.index(element) + if last_line[index] == "inf": + convergence_values.append(1e+20) + else: + convergence_values.append(last_line[index]) + adaptive_similartity_const = (1 + 1.0 / (np.log10(np.prod(np.array(limit_values,dtype=float)/np.array(convergence_values,dtype=float))) - 1))**3 * (1 - similarity_const) + similarity_const + else: + print("File not found in the current directory (A) or its subdirectories.") + adaptive_similartity_const = similarity_const + + self._logger.info("similarity_const: {} ".format(similarity_const)) + self._logger.info("adaptive_similartity_const: {} ".format(adaptive_similartity_const)) + + return adaptive_similartity_const + def _update_active_sims( self, similarity_dists: np.ndarray, @@ -84,7 +168,12 @@ def _update_active_sims( _is_sim_active : numpy array Updated 1D array having state (active or inactive) of each micro simulation """ - self._coarse_tol = self._coarse_const * self._refine_const * np.amax(similarity_dists)# adapt the constant factor here + if self._adaptive_coarse_const: + self._coarse_const = self._get_adaptive_similarity_const(self._coarse_const_input) + if self._adaptive_refine_const: + self._refine_const = self._get_adaptive_similarity_const(self._refine_const_input) + self._coarse_tol = self._coarse_const * self._refine_const * \ + np.amax(similarity_dists) _is_sim_active = np.copy(is_sim_active) # Input is_sim_active is not longer used after this point diff --git a/micro_manager/adaptivity/global_adaptivity.py b/micro_manager/adaptivity/global_adaptivity.py index 6164efdf..a1f58b18 100644 --- a/micro_manager/adaptivity/global_adaptivity.py +++ b/micro_manager/adaptivity/global_adaptivity.py @@ -186,6 +186,8 @@ def _update_inactive_sims( _sim_is_associated_to : numpy array 1D array with values of associated simulations of inactive simulations. Active simulations have None """ + if self._adaptive_refine_const: + self._refine_const = self._get_adaptive_similarity_const(self._refine_const_input) self._ref_tol = self._refine_const * np.amax(similarity_dists) _is_sim_active = np.copy(is_sim_active) # Input is_sim_active is not longer used after this point diff --git a/micro_manager/adaptivity/local_adaptivity.py b/micro_manager/adaptivity/local_adaptivity.py index 2f82c877..08c85811 100644 --- a/micro_manager/adaptivity/local_adaptivity.py +++ b/micro_manager/adaptivity/local_adaptivity.py @@ -100,6 +100,8 @@ def _update_inactive_sims( _sim_is_associated_to : numpy array 1D array with values of associated simulations of inactive simulations. Active simulations have None """ + if self._adaptive_refine_const: + self._refine_const = self._get_adaptive_similarity_const(self._refine_const_input) self._ref_tol = self._refine_const * np.amax(similarity_dists) _is_sim_active = np.copy(is_sim_active) # Input is_sim_active is not longer used after this point diff --git a/micro_manager/config.py b/micro_manager/config.py index d881c09e..6e98f921 100644 --- a/micro_manager/config.py +++ b/micro_manager/config.py @@ -44,6 +44,8 @@ def __init__(self, logger, config_filename): self._adaptivity_coarsening_constant = 0.5 self._adaptivity_refining_constant = 0.5 self._adaptivity_every_implicit_iteration = False + self._adaptivity_for_coarsening_constant = False + self._adaptivity_for_refining_constant = False self._adaptivity_similarity_measure = "L1" self.read_json(config_filename) @@ -134,7 +136,17 @@ def read_json(self, config_filename): self._adaptivity_similarity_measure = "L1" adaptivity_every_implicit_iteration = data["simulation_params"]["adaptivity"]["every_implicit_iteration"] - + try: + self._adaptivity_for_coarsening_constant = data["simulation_params"]["adaptivity"]["adaptive_coarsening_constant"] + self._logger.info("The adaptivity for coarsening constant is {}.".format(self._adaptivity_for_coarsening_constant)) + except: + self._logger.info("The adaptivity for coarsening constant is False as default.") + try: + self._adaptivity_for_refining_constant = data["simulation_params"]["adaptivity"]["adaptive_refining_constant"] + self._logger.info("The adaptivity for refining constant is {}.".format(self._adaptivity_for_refining_constant)) + except: + self._logger.info("The adaptivity for refining constant is False as default.") + if adaptivity_every_implicit_iteration == "True": self._adaptivity_every_implicit_iteration = True elif adaptivity_every_implicit_iteration == "False": @@ -349,6 +361,30 @@ def get_adaptivity_refining_const(self): Adaptivity refining constant """ return self._adaptivity_refining_constant + + def get_adaptivity_for_coarsening_const(self): + """ + Get adaptivity for coarsening constant. + More details: https://precice.org/tooling-micro-manager-configuration.html#adaptivity + + Returns + ------- + adaptivity_for_coarsening_constant : bool + Adaptivity for coarsening constant + """ + return self._adaptivity_for_coarsening_constant + + def get_adaptivity_for_refining_const(self): + """ + Get adaptivity for refining constant. + More details: https://precice.org/tooling-micro-manager-configuration.html#adaptivity + + Returns + ------- + adaptivity_for_refining_constant : bool + Adaptivity for refining constant + """ + return self._adaptivity_for_refining_constant def get_adaptivity_similarity_measure(self): """ From d6637c17bb13a6f8be431784e20be9b28387db1c Mon Sep 17 00:00:00 2001 From: Jun Chen Date: Tue, 29 Oct 2024 10:03:59 +0100 Subject: [PATCH 03/11] read config file in init --- micro_manager/adaptivity/adaptivity.py | 144 ++++++++++++++++--------- micro_manager/config.py | 44 ++++++-- 2 files changed, 131 insertions(+), 57 deletions(-) diff --git a/micro_manager/adaptivity/adaptivity.py b/micro_manager/adaptivity/adaptivity.py index 14bc24f4..0527c085 100644 --- a/micro_manager/adaptivity/adaptivity.py +++ b/micro_manager/adaptivity/adaptivity.py @@ -10,8 +10,6 @@ import xml.etree.ElementTree as ET from warnings import warn -import numpy as np - class AdaptivityCalculator: def __init__(self, configurator, logger) -> None: @@ -28,7 +26,7 @@ def __init__(self, configurator, logger) -> None: self._refine_const = self._refine_const_input self._coarse_const_input = configurator.get_adaptivity_coarsening_const() self._coarse_const = self._coarse_const_input - self._adaptive_coarse_const = configurator.get_adaptivity_for_coarsening_const() + self._adaptive_coarse_const = configurator.get_adaptivity_for_coarsening_const() self._adaptive_refine_const = configurator.get_adaptivity_for_refining_const() self._hist_param = configurator.get_adaptivity_hist_param() self._adaptivity_data_names = configurator.get_data_for_adaptivity() @@ -44,6 +42,38 @@ def __init__(self, configurator, logger) -> None: configurator.get_adaptivity_similarity_measure() ) + with open(self._config_file_name, 'r') as xml_file: + self.xml_data = xml_file.read() + + unique_names = ["absolute-convergence-measure", + "relative-convergence-measure", "residual-relative-convergence-measure"] + + # Initialize lists to store the found attributes + self.data_values = [] + self.limit_values = [] + + for unique_name in unique_names: + pattern = f'<{unique_name} limit="([^"]+)" data="([^"]+)" mesh="([^"]+)"' + matches = re.finditer(pattern, self.xml_data) + for match in matches: + self.data_values.append(match.group(2)) + self.limit_values.append(match.group(1)) + + # Check if any matches were found + if self.data_values and self.limit_values: + for i, (data_value, limit_value) in enumerate(zip(self.data_values, self.limit_values), start=1): + print(f"Match {i}:") + print(f"Data: {data_value}") + print(f"Limit: {limit_value}") + else: + print(f"No attributes found for unique name '{unique_name}'") + + def get_data_values(self): + return self.data_values + + def get_limit_values(self): + return self.limit_values + def _get_similarity_dists( self, dt: float, similarity_dists: np.ndarray, data: dict ) -> np.ndarray: @@ -85,37 +115,13 @@ def _get_adaptive_similarity_const(self, similarity_const: float) -> float: Returns ------- - adaptive_similartity_const : float + adapted_similarity_const : float """ - # Read the XML file as text - with open(self._config_file_name, 'r') as xml_file: - xml_data = xml_file.read() - - unique_names = ["absolute-convergence-measure","relative-convergence-measure","residual-relative-convergence-measure"] - - # Initialize lists to store the found attributes - data_values = [] - limit_values = [] - - for unique_name in unique_names: - patteren = f'<{unique_name} limit="([^"]+)" data="([^"]+)" mesh="([^"]+)"' - matches = re.finditer(patteren, xml_data) - for match in matches: - data_values.append(match.group(2)) - limit_values.append(match.group(1)) - - # Check if any matches were found - if data_values and limit_values: - for i, (data_value, limit_value) in enumerate(zip(data_values, limit_values), start=1): - print(f"Match {i}:") - print(f"Data: {data_value}") - print(f"Limit: {limit_value}") - else: - print(f"No attributes found for unique name '{unique_name}'") # read convergence value from precice-Mysolver-convergence.log file - # Initialize lists to store the extracted values - convergence_values = [] + convergence_values = [] # last iteration + convergence_values_LI = [] # last 2nd iteration + convergence_rate = 1.0 file_path = None file_name_suffix = "-convergence.log" @@ -126,34 +132,68 @@ def _get_adaptive_similarity_const(self, similarity_const: float) -> float: if file_name.endswith(file_name_suffix): file_path = os.path.join(root, file_name) break - + if file_path: with open(file_path, "r") as file: lines = file.readlines() if len(lines) < 2: print("File does not contain enough lines.") - adaptive_similartity_const = similarity_const + adaptive_similarity_const = similarity_const else: # Read the header line and last line of the file - header_line = lines[0].strip().split( ) # Assuming columns are tab-separated - last_line = lines[-1].strip().split( ) - for data in data_values: + # Assuming columns are tab-separated + header_line = lines[0].strip().split() + last_line = lines[-1].strip().split() + for data in self.data_values: for element in header_line: if data in element: index = header_line.index(element) if last_line[index] == "inf": convergence_values.append(1e+20) else: - convergence_values.append(last_line[index]) - adaptive_similartity_const = (1 + 1.0 / (np.log10(np.prod(np.array(limit_values,dtype=float)/np.array(convergence_values,dtype=float))) - 1))**3 * (1 - similarity_const) + similarity_const + index_config = self.data_values.index(data) + convergence_values.append(max( + float(last_line[index]), + float(self.limit_values[index_config]))) + min_convergence = np.log10(np.prod(np.array( + self.limit_values, dtype=float)/np.array(convergence_values, dtype=float))) + if last_line[1] == "60": + min_convergence = max(0.0, min_convergence) + + use_rate = False + alpha = 1 + if use_rate: + if int(last_line[1]) >= 2: + last_sec_line = lines[-2].strip().split() + for data in self.data_values: + for element in header_line: + if data in element: + index = header_line.index(element) + if last_sec_line[index] == "inf": + convergence_values_LI.append(1e+20) + else: + index_config = self.data_values.index( + data) + convergence_values_LI.append(max( + float(last_sec_line[index]), + float(self.limit_values[index_config]))) + min_convergence_LI = np.log10(np.prod(np.array( + self.limit_values, dtype=float)/np.array(convergence_values_LI, dtype=float))) + convergence_rate = min_convergence/min_convergence_LI + addtional = (1 + 1.0 / (min(0.0, min_convergence) - 1.0))**min( + (alpha/(convergence_rate), 10.0)) * (1 - similarity_const) + else: + addtional = ( + 1 + 1.0 / (min(0.0, min_convergence) - 1.0))**alpha * (1 - similarity_const) + adaptive_similarity_const = addtional + similarity_const else: - print("File not found in the current directory (A) or its subdirectories.") - adaptive_similartity_const = similarity_const - - self._logger.info("similarity_const: {} ".format(similarity_const)) - self._logger.info("adaptive_similartity_const: {} ".format(adaptive_similartity_const)) + print( + "Convergence log not found in the current directory (A) or its subdirectories.") - return adaptive_similartity_const + self._logger.info("adaptive_similarity_const: {} ".format( + adaptive_similarity_const)) + + return adaptive_similarity_const def _update_active_sims( self, similarity_dists: np.ndarray, is_sim_active: np.ndarray @@ -175,9 +215,13 @@ def _update_active_sims( Updated 1D array having state (active or inactive) of each micro simulation """ if self._adaptive_coarse_const: - self._coarse_const = self._get_adaptive_similarity_const(self._coarse_const_input) + self._coarse_const = self._get_adaptive_similarity_const( + self._coarse_const_input) + print(f"Adaptive coarse constant: {self._coarse_const}") if self._adaptive_refine_const: - self._refine_const = self._get_adaptive_similarity_const(self._refine_const_input) + self._refine_const = self._get_adaptive_similarity_const( + self._refine_const_input) + print(f"Adaptive refine constant: {self._refine_const}") max_similarity_dist = np.amax(similarity_dists) @@ -194,7 +238,7 @@ def _update_active_sims( _is_sim_active = np.copy( is_sim_active ) # Input is_sim_active is not longer used after this point - + # Update the set of active micro sims for i in range(_is_sim_active.size): if _is_sim_active[i]: # if sim is active @@ -380,7 +424,8 @@ def _l1rel(self, data: np.ndarray) -> np.ndarray: # divide by data to get relative difference # divide i,j by max(data[i],data[j]) to get relative difference relative = np.nan_to_num( - (pointwise_diff / np.maximum(data[np.newaxis, :], data[:, np.newaxis])) + (pointwise_diff / + np.maximum(data[np.newaxis, :], data[:, np.newaxis])) ) return np.linalg.norm(relative, ord=1, axis=-1) @@ -403,6 +448,7 @@ def _l2rel(self, data: np.ndarray) -> np.ndarray: # divide by data to get relative difference # divide i,j by max(data[i],data[j]) to get relative difference relative = np.nan_to_num( - (pointwise_diff / np.maximum(data[np.newaxis, :], data[:, np.newaxis])) + (pointwise_diff / + np.maximum(data[np.newaxis, :], data[:, np.newaxis])) ) return np.linalg.norm(relative, ord=2, axis=-1) diff --git a/micro_manager/config.py b/micro_manager/config.py index 5eba0c37..7a261654 100644 --- a/micro_manager/config.py +++ b/micro_manager/config.py @@ -182,16 +182,24 @@ def read_json(self, config_filename): ) self._adaptivity_similarity_measure = "L1" + adaptivity_for_coarsening_constant = "False" + adaptivity_for_refining_constant = "False" try: - self._adaptivity_for_coarsening_constant = data["simulation_params"]["adaptivity"]["adaptive_coarsening_constant"] - self._logger.info("The adaptivity for coarsening constant is {}.".format(self._adaptivity_for_coarsening_constant)) + adaptivity_for_coarsening_constant = data["simulation_params"][ + "adaptivity_settings" + ]["adaptive_coarsening_constant"] except: - self._logger.info("The adaptivity for coarsening constant is False as default.") + self._logger.info( + "The adaptivity for coarsening constant is False as default." + ) try: - self._adaptivity_for_refining_constant = data["simulation_params"]["adaptivity"]["adaptive_refining_constant"] - self._logger.info("The adaptivity for refining constant is {}.".format(self._adaptivity_for_refining_constant)) + adaptivity_for_refining_constant = data["simulation_params"][ + "adaptivity_settings" + ]["adaptive_refining_constant"] except: - self._logger.info("The adaptivity for refining constant is False as default.") + self._logger.info( + "The adaptivity for refining constant is False as default." + ) adaptivity_every_implicit_iteration = data["simulation_params"][ "adaptivity_settings" @@ -202,6 +210,26 @@ def read_json(self, config_filename): elif adaptivity_every_implicit_iteration == "False": self._adaptivity_every_implicit_iteration = False + if adaptivity_for_coarsening_constant == "True": + self._adaptivity_for_coarsening_constant = True + elif adaptivity_for_coarsening_constant == "False": + self._adaptivity_for_coarsening_constant = False + self._logger.info( + "The adaptivity for coarsening constant is {}.".format( + self._adaptivity_for_coarsening_constant + ) + ) + + if adaptivity_for_refining_constant == "True": + self._adaptivity_for_refining_constant = True + elif adaptivity_for_refining_constant == "False": + self._adaptivity_for_refining_constant = False + self._logger.info( + "The adaptivity for refining constant is {}.".format( + self._adaptivity_for_refining_constant + ) + ) + if not self._adaptivity_every_implicit_iteration: self._logger.info( "Micro Manager will compute adaptivity once at the start of every time window" @@ -421,7 +449,7 @@ def get_adaptivity_refining_const(self): Adaptivity refining constant """ return self._adaptivity_refining_constant - + def get_adaptivity_for_coarsening_const(self): """ Get adaptivity for coarsening constant. @@ -433,7 +461,7 @@ def get_adaptivity_for_coarsening_const(self): Adaptivity for coarsening constant """ return self._adaptivity_for_coarsening_constant - + def get_adaptivity_for_refining_const(self): """ Get adaptivity for refining constant. From 0de2fffd2ae7624f34942c2683e3cf4ae02b6f56 Mon Sep 17 00:00:00 2001 From: Jun Chen Date: Wed, 26 Mar 2025 17:31:10 +0100 Subject: [PATCH 04/11] improve implementation --- micro_manager/adaptivity/adaptivity.py | 131 +++++++++--------- micro_manager/adaptivity/global_adaptivity.py | 2 - micro_manager/adaptivity/local_adaptivity.py | 23 ++- micro_manager/config.py | 7 +- micro_manager/micro_manager.py | 13 ++ 5 files changed, 104 insertions(+), 72 deletions(-) diff --git a/micro_manager/adaptivity/adaptivity.py b/micro_manager/adaptivity/adaptivity.py index ca0f3399..2582893f 100644 --- a/micro_manager/adaptivity/adaptivity.py +++ b/micro_manager/adaptivity/adaptivity.py @@ -32,6 +32,9 @@ def __init__(self, configurator, logger) -> None: self._adaptivity_data_names = configurator.get_data_for_adaptivity() self._adaptivity_type = configurator.get_adaptivity_type() self._config_file_name = configurator.get_config_file_name() + self._convergence_measure = [] + self._convergence_status = [] + self._min_addition = 1.0 self._logger = logger @@ -109,7 +112,7 @@ def _get_similarity_dists( return exp(-self._hist_param * dt) * _similarity_dists + dt * data_diff - def _get_adaptive_similarity_const(self, similarity_const: float) -> float: + def _get_addition(self, similarity_const: float) -> float: """ Get adapted coarsening/refining constant based on limit values in preCICE configuration file and convergence measurements in preCICE @@ -120,30 +123,28 @@ def _get_adaptive_similarity_const(self, similarity_const: float) -> float: # read convergence value from precice-Mysolver-convergence.log file convergence_values = [] # last iteration - convergence_values_LI = [] # last 2nd iteration - convergence_rate = 1.0 + # convergence_rate = 1.0 + additional = 0.0 file_path = None file_name_suffix = "-convergence.log" - # Search for the file in the current directory and its subdirectories for root, _, files in os.walk(os.getcwd()): for file_name in files: if file_name.endswith(file_name_suffix): file_path = os.path.join(root, file_name) break - - if file_path: - with open(file_path, "r") as file: - lines = file.readlines() - if len(lines) < 2: - print("File does not contain enough lines.") - adaptive_similarity_const = similarity_const - else: - # Read the header line and last line of the file - # Assuming columns are tab-separated - header_line = lines[0].strip().split() - last_line = lines[-1].strip().split() + with open(file_path, "r") as file: + lines = file.readlines() + if len(lines) < 1: + print("File is empty.") + else: + if len(lines) > len(self._convergence_measure): + if len(lines) == 2: + self._convergence_measure.append(lines[0].strip().split()) + self._convergence_measure.append(lines[-1].strip().split()) + header_line = self._convergence_measure[0] + last_line = self._convergence_measure[-1] for data in self.data_values: for element in header_line: if data in element: @@ -159,45 +160,46 @@ def _get_adaptive_similarity_const(self, similarity_const: float) -> float: self.limit_values, dtype=float)/np.array(convergence_values, dtype=float))) if last_line[1] == "60": min_convergence = max(0.0, min_convergence) - - use_rate = False - alpha = 1 - if use_rate: - if int(last_line[1]) >= 2: - last_sec_line = lines[-2].strip().split() - for data in self.data_values: - for element in header_line: - if data in element: - index = header_line.index(element) - if last_sec_line[index] == "inf": - convergence_values_LI.append(1e+20) - else: - index_config = self.data_values.index( - data) - convergence_values_LI.append(max( - float(last_sec_line[index]), - float(self.limit_values[index_config]))) - min_convergence_LI = np.log10(np.prod(np.array( - self.limit_values, dtype=float)/np.array(convergence_values_LI, dtype=float))) - convergence_rate = min_convergence/min_convergence_LI - addtional = (1 + 1.0 / (min(0.0, min_convergence) - 1.0))**min( - (alpha/(convergence_rate), 10.0)) * (1 - similarity_const) - else: - addtional = ( - 1 + 1.0 / (min(0.0, min_convergence) - 1.0))**alpha * (1 - similarity_const) - adaptive_similarity_const = addtional + similarity_const - else: - print( - "Convergence log not found in the current directory (A) or its subdirectories.") - - self._logger.info("adaptive_similarity_const: {} ".format( - adaptive_similarity_const)) - - return adaptive_similarity_const + self._convergence_status.append(min_convergence) + + # use_rate = 1 # 0 for no use, 1 for divide, 2 for multiplication + alpha = 3.0 + # convergence_rate = 1.0 + + # if use_rate > 0 and len(self._convergence_status) > 2: + # if abs(self._convergence_status[-1]) and abs(self._convergence_status[-2]) > 1e-10: + # convergence_rate = self._convergence_status[-1]/self._convergence_status[-2] + # self._logger.info("min Convergence: {} convergence rate: {} ".format( + # self._convergence_status[-1], convergence_rate)) + # if use_rate == 1: + # additional = (1 + 1.0 / (min(0.0, self._convergence_status[-1]) - 1.0))**min((alpha/(convergence_rate), 10.0)) + # elif use_rate == 2: + # additional = (1 + 1.0 / (min(0.0, self._convergence_status[-1]) - 1.0))**(alpha*convergence_rate) + # else: + # print("use_rate setting not accepted") + # else: + # if len(self._convergence_status) <= 2: + # self._min_addition = 1.0 + # self._logger.info("less than two iterations, relax") + # self._logger.info("min Convergence: {} ".format(self._convergence_status[-1])) + # additional = (1 + 1.0 / (min(0.0, self._convergence_status[-1]) - 1.0))**alpha + if len(self._convergence_status) <= 2: + self._min_addition = 1.0 + self._logger.info("less than two iterations, relax") + self._logger.info("min Convergence: {} ".format(self._convergence_status[-1])) + additional = (1 + 1.0 / (min(0.0, self._convergence_status[-1]) - 1.0))**alpha + + return additional + + def _get_adaptive_coarsening_const(self) -> float: + return self._coarse_const + + def _get_adaptive_refining_const(self) -> float: + return self._refine_const def _update_active_sims( - self, similarity_dists: np.ndarray, is_sim_active: np.ndarray - ) -> np.ndarray: + self, similarity_dists: np.ndarray, is_sim_active: np.ndarray, use_dyn_coarse_tol: bool = False, use_dyn_ref_tol: bool = False + ) -> tuple: """ Update set of active micro simulations. Active micro simulations are compared to each other and if found similar, one of them is deactivated. @@ -214,14 +216,17 @@ def _update_active_sims( _is_sim_active : numpy array Updated 1D array having state (active or inactive) of each micro simulation """ - if self._adaptive_coarse_const: - self._coarse_const = self._get_adaptive_similarity_const( - self._coarse_const_input) - print(f"Adaptive coarse constant: {self._coarse_const}") - if self._adaptive_refine_const: - self._refine_const = self._get_adaptive_similarity_const( - self._refine_const_input) - print(f"Adaptive refine constant: {self._refine_const}") + if use_dyn_ref_tol and self._adaptive_refine_const: + additional = self._get_addition(self._refine_const_input)*(1-self._refine_const_input) + self._min_addition = min(self._min_addition, additional) + additional = self._min_addition + if additional > 0.0: + _refine_const = additional+ self._refine_const_input + else: + _refine_const = self._refine_const_input + self._logger.info("Adaptive refine constant: {}".format(self._refine_const)) + else: + _refine_const = self._refine_const_input max_similarity_dist = np.amax(similarity_dists) @@ -232,7 +237,7 @@ def _update_active_sims( self._coarse_tol = sys.float_info.min else: self._coarse_tol = ( - self._coarse_const * self._refine_const * max_similarity_dist + self._coarse_const * _refine_const * max_similarity_dist ) _is_sim_active = np.copy( @@ -245,7 +250,7 @@ def _update_active_sims( if self._check_for_deactivation(i, similarity_dists, _is_sim_active): _is_sim_active[i] = False - return _is_sim_active + return _is_sim_active, _refine_const def _associate_inactive_to_active( self, diff --git a/micro_manager/adaptivity/global_adaptivity.py b/micro_manager/adaptivity/global_adaptivity.py index 9d4d39ba..90690ec7 100644 --- a/micro_manager/adaptivity/global_adaptivity.py +++ b/micro_manager/adaptivity/global_adaptivity.py @@ -227,8 +227,6 @@ def _update_inactive_sims( _sim_is_associated_to : numpy array 1D array with values of associated simulations of inactive simulations. Active simulations have None """ - if self._adaptive_refine_const: - self._refine_const = self._get_adaptive_similarity_const(self._refine_const_input) self._ref_tol = self._refine_const * np.amax(similarity_dists) _is_sim_active = np.copy( diff --git a/micro_manager/adaptivity/local_adaptivity.py b/micro_manager/adaptivity/local_adaptivity.py index a1b04622..99b7973d 100644 --- a/micro_manager/adaptivity/local_adaptivity.py +++ b/micro_manager/adaptivity/local_adaptivity.py @@ -70,12 +70,24 @@ def compute_adaptivity( ) # Operation done globally if global adaptivity is chosen - is_sim_active = self._update_active_sims(similarity_dists, is_sim_active_nm1) + is_sim_active_dyn, refine_const_dyn = self._update_active_sims(similarity_dists, is_sim_active_nm1, False, True) + is_sim_active_dyn, sim_is_associated_to_dyn = self._update_inactive_sims( + similarity_dists, is_sim_active_dyn, sim_is_associated_to_nm1, micro_sims, refine_const_dyn + ) - is_sim_active, sim_is_associated_to = self._update_inactive_sims( - similarity_dists, is_sim_active, sim_is_associated_to_nm1, micro_sims + is_sim_active_sta, refine_const_sta = self._update_active_sims(similarity_dists, is_sim_active_nm1, False, False) + is_sim_active_sta, sim_is_associated_to_sta = self._update_inactive_sims(similarity_dists, is_sim_active_sta, sim_is_associated_to_nm1, micro_sims, refine_const_sta ) + if np.array_equal(is_sim_active_dyn, is_sim_active_sta) and np.array_equal(sim_is_associated_to_dyn, sim_is_associated_to_sta): + is_sim_active = is_sim_active_sta + sim_is_associated_to = sim_is_associated_to_sta + self._refine_const = refine_const_sta + else: + is_sim_active = is_sim_active_dyn + sim_is_associated_to = sim_is_associated_to_dyn + self._refine_const = refine_const_dyn + sim_is_associated_to = self._associate_inactive_to_active( similarity_dists, is_sim_active, sim_is_associated_to ) @@ -95,6 +107,7 @@ def _update_inactive_sims( is_sim_active: np.ndarray, sim_is_associated_to: np.ndarray, micro_sims: list, + refine_const: float, ) -> tuple: """ Update set of inactive micro simulations. Each inactive micro simulation is compared to all active ones @@ -118,9 +131,7 @@ def _update_inactive_sims( _sim_is_associated_to : numpy array 1D array with values of associated simulations of inactive simulations. Active simulations have None """ - if self._adaptive_refine_const: - self._refine_const = self._get_adaptive_similarity_const(self._refine_const_input) - self._ref_tol = self._refine_const * np.amax(similarity_dists) + self._ref_tol = refine_const * np.amax(similarity_dists) _is_sim_active = np.copy( is_sim_active diff --git a/micro_manager/config.py b/micro_manager/config.py index a458b593..b0241686 100644 --- a/micro_manager/config.py +++ b/micro_manager/config.py @@ -221,7 +221,7 @@ def read_json_micro_manager(self): ) self._adaptivity_similarity_measure = "L1" - adaptivity_every_implicit_iteration = self.data["simulation_params"][ + adaptivity_every_implicit_iteration = self._data["simulation_params"][ "adaptivity_settings" ]["every_implicit_iteration"] @@ -230,6 +230,8 @@ def read_json_micro_manager(self): elif adaptivity_every_implicit_iteration == "False": self._adaptivity_every_implicit_iteration = False + adaptivity_for_coarsening_constant = self._data["simulation_params"]["adaptivity_settings"]["adaptive_coarsening_constant"] + if adaptivity_for_coarsening_constant == "True": self._adaptivity_for_coarsening_constant = True elif adaptivity_for_coarsening_constant == "False": @@ -240,6 +242,7 @@ def read_json_micro_manager(self): ) ) + adaptivity_for_refining_constant = self._data["simulation_params"]["adaptivity_settings"]["adaptive_refining_constant"] if adaptivity_for_refining_constant == "True": self._adaptivity_for_refining_constant = True elif adaptivity_for_refining_constant == "False": @@ -257,6 +260,8 @@ def read_json_micro_manager(self): self._write_data_names["active_state"] = False self._write_data_names["active_steps"] = False + self._write_data_names["coarse_const"] = 0.0 + self._write_data_names["refine_const"] = 0.0 if "interpolate_crash" in self._data["simulation_params"]: if self._data["simulation_params"]["interpolate_crash"] == "True": diff --git a/micro_manager/micro_manager.py b/micro_manager/micro_manager.py index 7e4ad20e..8b85a8a7 100644 --- a/micro_manager/micro_manager.py +++ b/micro_manager/micro_manager.py @@ -294,6 +294,8 @@ def solve(self) -> None: t, ) ) + if self._is_adaptivity_on: + self._adaptivity_controller._convergence_status = [] # Clear convergence status if self._micro_sims_have_output: if n % self._micro_n_out == 0: @@ -848,6 +850,17 @@ def _solve_micro_simulations_with_adaptivity( for name in self._adaptivity_micro_data_names: self._data_for_adaptivity[name][i] = micro_sims_output[i][name] + # Add similarity constants to the output + car_const = self._adaptivity_controller._get_adaptive_coarsening_const() + ref_const = self._adaptivity_controller._get_adaptive_refining_const() + self._logger.info("getting adaptivity constants to send to macro {}, {}".format(car_const,ref_const)) + for inactive_id in inactive_sim_ids: + micro_sims_output[inactive_id]["coarse_const"] = car_const + micro_sims_output[inactive_id]["refine_const"] = ref_const + for active_id in active_sim_ids: + micro_sims_output[active_id]["coarse_const"] = car_const + micro_sims_output[active_id]["refine_const"] = ref_const + return micro_sims_output def _interpolate_output_for_crashed_sim( From 788b049065e4bfb92c3915d7f8196df5639f0dff Mon Sep 17 00:00:00 2001 From: Jun Chen Date: Fri, 28 Mar 2025 17:33:46 +0100 Subject: [PATCH 05/11] remove dynamic coarsening constant --- micro_manager/adaptivity/adaptivity.py | 11 +++----- micro_manager/adaptivity/global_adaptivity.py | 22 +++++++++++++--- micro_manager/adaptivity/local_adaptivity.py | 4 +-- micro_manager/config.py | 26 ------------------- micro_manager/micro_manager.py | 5 +--- 5 files changed, 24 insertions(+), 44 deletions(-) diff --git a/micro_manager/adaptivity/adaptivity.py b/micro_manager/adaptivity/adaptivity.py index 2582893f..d4e584c3 100644 --- a/micro_manager/adaptivity/adaptivity.py +++ b/micro_manager/adaptivity/adaptivity.py @@ -24,10 +24,8 @@ def __init__(self, configurator, logger) -> None: """ self._refine_const_input = configurator.get_adaptivity_refining_const() self._refine_const = self._refine_const_input - self._coarse_const_input = configurator.get_adaptivity_coarsening_const() - self._coarse_const = self._coarse_const_input - self._adaptive_coarse_const = configurator.get_adaptivity_for_coarsening_const() self._adaptive_refine_const = configurator.get_adaptivity_for_refining_const() + self._coarse_const = configurator.get_adaptivity_coarsening_const() self._hist_param = configurator.get_adaptivity_hist_param() self._adaptivity_data_names = configurator.get_data_for_adaptivity() self._adaptivity_type = configurator.get_adaptivity_type() @@ -114,7 +112,7 @@ def _get_similarity_dists( def _get_addition(self, similarity_const: float) -> float: """ - Get adapted coarsening/refining constant based on limit values in preCICE configuration file and convergence measurements in preCICE + Get adapted refining constant based on limit values in preCICE configuration file and convergence measurements in preCICE Returns ------- @@ -191,14 +189,11 @@ def _get_addition(self, similarity_const: float) -> float: return additional - def _get_adaptive_coarsening_const(self) -> float: - return self._coarse_const - def _get_adaptive_refining_const(self) -> float: return self._refine_const def _update_active_sims( - self, similarity_dists: np.ndarray, is_sim_active: np.ndarray, use_dyn_coarse_tol: bool = False, use_dyn_ref_tol: bool = False + self, similarity_dists: np.ndarray, is_sim_active: np.ndarray, use_dyn_ref_tol: bool = False ) -> tuple: """ Update set of active micro simulations. Active micro simulations are compared to each other diff --git a/micro_manager/adaptivity/global_adaptivity.py b/micro_manager/adaptivity/global_adaptivity.py index 90690ec7..b49d5ce6 100644 --- a/micro_manager/adaptivity/global_adaptivity.py +++ b/micro_manager/adaptivity/global_adaptivity.py @@ -118,11 +118,24 @@ def compute_adaptivity( dt, similarity_dists_nm1, global_data_for_adaptivity ) - is_sim_active = self._update_active_sims(similarity_dists, is_sim_active_nm1) + is_sim_active_dyn, refine_const_dyn = self._update_active_sims(similarity_dists, is_sim_active_nm1, True) + is_sim_active_dyn, sim_is_associated_to_dyn = self._update_inactive_sims( + similarity_dists, is_sim_active_dyn, sim_is_associated_to_nm1, micro_sims, refine_const_dyn + ) - is_sim_active, sim_is_associated_to = self._update_inactive_sims( - similarity_dists, is_sim_active, sim_is_associated_to_nm1, micro_sims + is_sim_active_sta, refine_const_sta = self._update_active_sims(similarity_dists, is_sim_active_nm1, False) + is_sim_active_sta, sim_is_associated_to_sta = self._update_inactive_sims(similarity_dists, is_sim_active_sta, sim_is_associated_to_nm1, micro_sims, refine_const_sta ) + + if np.array_equal(is_sim_active_dyn, is_sim_active_sta) and np.array_equal(sim_is_associated_to_dyn, sim_is_associated_to_sta): + is_sim_active = is_sim_active_sta + sim_is_associated_to = sim_is_associated_to_sta + self._refine_const = refine_const_sta + else: + is_sim_active = is_sim_active_dyn + sim_is_associated_to = sim_is_associated_to_dyn + self._refine_const = refine_const_dyn + sim_is_associated_to = self._associate_inactive_to_active( similarity_dists, is_sim_active, sim_is_associated_to ) @@ -204,6 +217,7 @@ def _update_inactive_sims( is_sim_active: np.ndarray, sim_is_associated_to: np.ndarray, micro_sims: list, + refine_const: float, ) -> tuple: """ Update set of inactive micro simulations. Each inactive micro simulation is compared to all active ones @@ -227,7 +241,7 @@ def _update_inactive_sims( _sim_is_associated_to : numpy array 1D array with values of associated simulations of inactive simulations. Active simulations have None """ - self._ref_tol = self._refine_const * np.amax(similarity_dists) + self._ref_tol = refine_const * np.amax(similarity_dists) _is_sim_active = np.copy( is_sim_active diff --git a/micro_manager/adaptivity/local_adaptivity.py b/micro_manager/adaptivity/local_adaptivity.py index 99b7973d..b20e0caa 100644 --- a/micro_manager/adaptivity/local_adaptivity.py +++ b/micro_manager/adaptivity/local_adaptivity.py @@ -70,12 +70,12 @@ def compute_adaptivity( ) # Operation done globally if global adaptivity is chosen - is_sim_active_dyn, refine_const_dyn = self._update_active_sims(similarity_dists, is_sim_active_nm1, False, True) + is_sim_active_dyn, refine_const_dyn = self._update_active_sims(similarity_dists, is_sim_active_nm1, True) is_sim_active_dyn, sim_is_associated_to_dyn = self._update_inactive_sims( similarity_dists, is_sim_active_dyn, sim_is_associated_to_nm1, micro_sims, refine_const_dyn ) - is_sim_active_sta, refine_const_sta = self._update_active_sims(similarity_dists, is_sim_active_nm1, False, False) + is_sim_active_sta, refine_const_sta = self._update_active_sims(similarity_dists, is_sim_active_nm1, False) is_sim_active_sta, sim_is_associated_to_sta = self._update_inactive_sims(similarity_dists, is_sim_active_sta, sim_is_associated_to_nm1, micro_sims, refine_const_sta ) diff --git a/micro_manager/config.py b/micro_manager/config.py index b0241686..29fe0347 100644 --- a/micro_manager/config.py +++ b/micro_manager/config.py @@ -48,7 +48,6 @@ def __init__(self, logger, config_filename): self._adaptivity_coarsening_constant = 0.5 self._adaptivity_refining_constant = 0.5 self._adaptivity_every_implicit_iteration = False - self._adaptivity_for_coarsening_constant = False self._adaptivity_for_refining_constant = False self._adaptivity_similarity_measure = "L1" @@ -230,18 +229,6 @@ def read_json_micro_manager(self): elif adaptivity_every_implicit_iteration == "False": self._adaptivity_every_implicit_iteration = False - adaptivity_for_coarsening_constant = self._data["simulation_params"]["adaptivity_settings"]["adaptive_coarsening_constant"] - - if adaptivity_for_coarsening_constant == "True": - self._adaptivity_for_coarsening_constant = True - elif adaptivity_for_coarsening_constant == "False": - self._adaptivity_for_coarsening_constant = False - self._logger.info( - "The adaptivity for coarsening constant is {}.".format( - self._adaptivity_for_coarsening_constant - ) - ) - adaptivity_for_refining_constant = self._data["simulation_params"]["adaptivity_settings"]["adaptive_refining_constant"] if adaptivity_for_refining_constant == "True": self._adaptivity_for_refining_constant = True @@ -260,7 +247,6 @@ def read_json_micro_manager(self): self._write_data_names["active_state"] = False self._write_data_names["active_steps"] = False - self._write_data_names["coarse_const"] = 0.0 self._write_data_names["refine_const"] = 0.0 if "interpolate_crash" in self._data["simulation_params"]: @@ -515,18 +501,6 @@ def get_adaptivity_refining_const(self): """ return self._adaptivity_refining_constant - def get_adaptivity_for_coarsening_const(self): - """ - Get adaptivity for coarsening constant. - More details: https://precice.org/tooling-micro-manager-configuration.html#adaptivity - - Returns - ------- - adaptivity_for_coarsening_constant : bool - Adaptivity for coarsening constant - """ - return self._adaptivity_for_coarsening_constant - def get_adaptivity_for_refining_const(self): """ Get adaptivity for refining constant. diff --git a/micro_manager/micro_manager.py b/micro_manager/micro_manager.py index 8b85a8a7..cbfddb5a 100644 --- a/micro_manager/micro_manager.py +++ b/micro_manager/micro_manager.py @@ -851,14 +851,11 @@ def _solve_micro_simulations_with_adaptivity( self._data_for_adaptivity[name][i] = micro_sims_output[i][name] # Add similarity constants to the output - car_const = self._adaptivity_controller._get_adaptive_coarsening_const() ref_const = self._adaptivity_controller._get_adaptive_refining_const() - self._logger.info("getting adaptivity constants to send to macro {}, {}".format(car_const,ref_const)) + self._logger.info("getting adaptivity constants to send to macro {}".format(ref_const)) for inactive_id in inactive_sim_ids: - micro_sims_output[inactive_id]["coarse_const"] = car_const micro_sims_output[inactive_id]["refine_const"] = ref_const for active_id in active_sim_ids: - micro_sims_output[active_id]["coarse_const"] = car_const micro_sims_output[active_id]["refine_const"] = ref_const return micro_sims_output From bfef08e23c562098b36b17727919103ff0bc0e74 Mon Sep 17 00:00:00 2001 From: Jun Chen Date: Wed, 6 Aug 2025 13:50:54 +0200 Subject: [PATCH 06/11] formatting --- micro_manager/adaptivity/adaptivity.py | 58 ++++++++++++------- micro_manager/adaptivity/global_adaptivity.py | 25 ++++++-- micro_manager/adaptivity/local_adaptivity.py | 25 ++++++-- micro_manager/config.py | 7 ++- micro_manager/micro_manager.py | 4 +- 5 files changed, 83 insertions(+), 36 deletions(-) diff --git a/micro_manager/adaptivity/adaptivity.py b/micro_manager/adaptivity/adaptivity.py index 024dd51d..e00bae9e 100644 --- a/micro_manager/adaptivity/adaptivity.py +++ b/micro_manager/adaptivity/adaptivity.py @@ -69,7 +69,7 @@ def __init__(self, configurator, rank, nsims) -> None: self._similarity_measure = self._get_similarity_measure( configurator.get_adaptivity_similarity_measure() ) - + output_dir = configurator.get_output_dir() if output_dir is not None: @@ -103,11 +103,14 @@ def __init__(self, configurator, rank, nsims) -> None: csv_logger=True, ) - with open(self._config_file_name, 'r') as xml_file: + with open(self._config_file_name, "r") as xml_file: self.xml_data = xml_file.read() - unique_names = ["absolute-convergence-measure", - "relative-convergence-measure", "residual-relative-convergence-measure"] + unique_names = [ + "absolute-convergence-measure", + "relative-convergence-measure", + "residual-relative-convergence-measure", + ] # Initialize lists to store the found attributes self.data_values = [] @@ -122,7 +125,9 @@ def __init__(self, configurator, rank, nsims) -> None: # Check if any matches were found if self.data_values and self.limit_values: - for i, (data_value, limit_value) in enumerate(zip(self.data_values, self.limit_values), start=1): + for i, (data_value, limit_value) in enumerate( + zip(self.data_values, self.limit_values), start=1 + ): print(f"Match {i}:") print(f"Data: {data_value}") print(f"Limit: {limit_value}") @@ -197,14 +202,21 @@ def _get_addition(self, similarity_const: float) -> float: if data in element: index = header_line.index(element) if last_line[index] == "inf": - convergence_values.append(1e+20) + convergence_values.append(1e20) else: index_config = self.data_values.index(data) - convergence_values.append(max( - float(last_line[index]), - float(self.limit_values[index_config]))) - min_convergence = np.log10(np.prod(np.array( - self.limit_values, dtype=float)/np.array(convergence_values, dtype=float))) + convergence_values.append( + max( + float(last_line[index]), + float(self.limit_values[index_config]), + ) + ) + min_convergence = np.log10( + np.prod( + np.array(self.limit_values, dtype=float) + / np.array(convergence_values, dtype=float) + ) + ) if last_line[1] == "60": min_convergence = max(0.0, min_convergence) self._convergence_status.append(min_convergence) @@ -214,8 +226,12 @@ def _get_addition(self, similarity_const: float) -> float: if len(self._convergence_status) <= 2: self._min_addition = 1.0 self._logger.info("less than two iterations, relax") - self._logger.info("min Convergence: {} ".format(self._convergence_status[-1])) - additional = (1 + 1.0 / (min(0.0, self._convergence_status[-1]) - 1.0))**alpha + self._logger.info( + "min Convergence: {} ".format(self._convergence_status[-1]) + ) + additional = ( + 1 + 1.0 / (min(0.0, self._convergence_status[-1]) - 1.0) + ) ** alpha return additional @@ -228,11 +244,13 @@ def _update_active_sims(self, use_dyn_ref_tol: bool = False) -> None: and if found similar, one of them is deactivated. """ if use_dyn_ref_tol and self._adaptive_refine_const: - additional = self._get_addition(self._refine_const_input)*(1-self._refine_const_input) + additional = self._get_addition(self._refine_const_input) * ( + 1 - self._refine_const_input + ) self._min_addition = min(self._min_addition, additional) additional = self._min_addition if additional > 0.0: - _refine_const = additional+ self._refine_const_input + _refine_const = additional + self._refine_const_input else: _refine_const = self._refine_const_input self._logger.info("Adaptive refine constant: {}".format(self._refine_const)) @@ -247,9 +265,7 @@ def _update_active_sims(self, use_dyn_ref_tol: bool = False) -> None: ) self._coarse_tol = sys.float_info.min else: - self._coarse_tol = ( - self._coarse_const * _refine_const * max_similarity_dist - ) + self._coarse_tol = self._coarse_const * _refine_const * max_similarity_dist # Update the set of active micro sims for i in range(self._is_sim_active.size): @@ -416,8 +432,7 @@ def _l1rel(self, data: np.ndarray) -> np.ndarray: ( pointwise_diff / np.maximum( - np.absolute(data[np.newaxis, :]), np.absolute( - data[:, np.newaxis]) + np.absolute(data[np.newaxis, :]), np.absolute(data[:, np.newaxis]) ) ) ) @@ -445,8 +460,7 @@ def _l2rel(self, data: np.ndarray) -> np.ndarray: ( pointwise_diff / np.maximum( - np.absolute(data[np.newaxis, :]), np.absolute( - data[:, np.newaxis]) + np.absolute(data[np.newaxis, :]), np.absolute(data[:, np.newaxis]) ) ) ) diff --git a/micro_manager/adaptivity/global_adaptivity.py b/micro_manager/adaptivity/global_adaptivity.py index 51ee0b34..74e3052f 100644 --- a/micro_manager/adaptivity/global_adaptivity.py +++ b/micro_manager/adaptivity/global_adaptivity.py @@ -151,16 +151,31 @@ def compute_adaptivity( ): # Only the first rank in the node updates the similarity distances self._update_similarity_dists(dt, global_data_for_adaptivity) - is_sim_active_dyn, refine_const_dyn = self._update_active_sims(similarity_dists, is_sim_active_nm1, True) + is_sim_active_dyn, refine_const_dyn = self._update_active_sims( + similarity_dists, is_sim_active_nm1, True + ) is_sim_active_dyn, sim_is_associated_to_dyn = self._update_inactive_sims( - similarity_dists, is_sim_active_dyn, sim_is_associated_to_nm1, micro_sims, refine_const_dyn + similarity_dists, + is_sim_active_dyn, + sim_is_associated_to_nm1, + micro_sims, + refine_const_dyn, ) - is_sim_active_sta, refine_const_sta = self._update_active_sims(similarity_dists, is_sim_active_nm1, False) - is_sim_active_sta, sim_is_associated_to_sta = self._update_inactive_sims(similarity_dists, is_sim_active_sta, sim_is_associated_to_nm1, micro_sims, refine_const_sta + is_sim_active_sta, refine_const_sta = self._update_active_sims( + similarity_dists, is_sim_active_nm1, False + ) + is_sim_active_sta, sim_is_associated_to_sta = self._update_inactive_sims( + similarity_dists, + is_sim_active_sta, + sim_is_associated_to_nm1, + micro_sims, + refine_const_sta, ) - if np.array_equal(is_sim_active_dyn, is_sim_active_sta) and np.array_equal(sim_is_associated_to_dyn, sim_is_associated_to_sta): + if np.array_equal(is_sim_active_dyn, is_sim_active_sta) and np.array_equal( + sim_is_associated_to_dyn, sim_is_associated_to_sta + ): is_sim_active = is_sim_active_sta sim_is_associated_to = sim_is_associated_to_sta self._refine_const = refine_const_sta diff --git a/micro_manager/adaptivity/local_adaptivity.py b/micro_manager/adaptivity/local_adaptivity.py index 0a1f8cad..99ec3f87 100644 --- a/micro_manager/adaptivity/local_adaptivity.py +++ b/micro_manager/adaptivity/local_adaptivity.py @@ -78,16 +78,31 @@ def compute_adaptivity( self._update_similarity_dists(dt, data_for_adaptivity) # Operation done globally if global adaptivity is chosen - is_sim_active_dyn, refine_const_dyn = self._update_active_sims(similarity_dists, is_sim_active_nm1, True) + is_sim_active_dyn, refine_const_dyn = self._update_active_sims( + similarity_dists, is_sim_active_nm1, True + ) is_sim_active_dyn, sim_is_associated_to_dyn = self._update_inactive_sims( - similarity_dists, is_sim_active_dyn, sim_is_associated_to_nm1, micro_sims, refine_const_dyn + similarity_dists, + is_sim_active_dyn, + sim_is_associated_to_nm1, + micro_sims, + refine_const_dyn, ) - is_sim_active_sta, refine_const_sta = self._update_active_sims(similarity_dists, is_sim_active_nm1, False) - is_sim_active_sta, sim_is_associated_to_sta = self._update_inactive_sims(similarity_dists, is_sim_active_sta, sim_is_associated_to_nm1, micro_sims, refine_const_sta + is_sim_active_sta, refine_const_sta = self._update_active_sims( + similarity_dists, is_sim_active_nm1, False + ) + is_sim_active_sta, sim_is_associated_to_sta = self._update_inactive_sims( + similarity_dists, + is_sim_active_sta, + sim_is_associated_to_nm1, + micro_sims, + refine_const_sta, ) - if np.array_equal(is_sim_active_dyn, is_sim_active_sta) and np.array_equal(sim_is_associated_to_dyn, sim_is_associated_to_sta): + if np.array_equal(is_sim_active_dyn, is_sim_active_sta) and np.array_equal( + sim_is_associated_to_dyn, sim_is_associated_to_sta + ): is_sim_active = is_sim_active_sta sim_is_associated_to = sim_is_associated_to_sta self._refine_const = refine_const_sta diff --git a/micro_manager/config.py b/micro_manager/config.py index 951a70e5..b8904177 100644 --- a/micro_manager/config.py +++ b/micro_manager/config.py @@ -251,8 +251,7 @@ def read_json_micro_manager(self): ): self._adaptivity_type = "global" else: - raise Exception( - "Adaptivity type can be either local or global.") + raise Exception("Adaptivity type can be either local or global.") self._logger.log_info_rank_zero("Adaptivity type: " + self._adaptivity_type) @@ -376,7 +375,9 @@ def read_json_micro_manager(self): "Micro Manager will compute adaptivity in every implicit iteration, if implicit coupling is done." ) - adaptivity_for_refining_constant = self._data["simulation_params"]["adaptivity_settings"]["adaptive_refining_constant"] + adaptivity_for_refining_constant = self._data["simulation_params"][ + "adaptivity_settings" + ]["adaptive_refining_constant"] if adaptivity_for_refining_constant: self._adaptivity_for_refining_constant = True elif adaptivity_for_refining_constant: diff --git a/micro_manager/micro_manager.py b/micro_manager/micro_manager.py index ee9a9728..b4eb1276 100644 --- a/micro_manager/micro_manager.py +++ b/micro_manager/micro_manager.py @@ -914,7 +914,9 @@ def _solve_micro_simulations_with_adaptivity( # Add similarity constants to the output ref_const = self._adaptivity_controller._get_adaptive_refining_const() - self._logger.info("getting adaptivity constants to send to macro {}".format(ref_const)) + self._logger.info( + "getting adaptivity constants to send to macro {}".format(ref_const) + ) for inactive_id in inactive_sim_ids: micro_sims_output[inactive_id]["refine_const"] = ref_const for active_id in active_sim_ids: From b8e167709c68cdf61536e3f3236bb027decb4c15 Mon Sep 17 00:00:00 2001 From: Jun Chen Date: Fri, 15 Aug 2025 11:25:02 +0200 Subject: [PATCH 07/11] refactor --- micro_manager/adaptivity/adaptivity.py | 224 +++++++++++------- micro_manager/adaptivity/global_adaptivity.py | 168 +++++++++---- micro_manager/adaptivity/local_adaptivity.py | 109 ++++----- micro_manager/config.py | 31 +-- micro_manager/micro_manager.py | 5 +- 5 files changed, 317 insertions(+), 220 deletions(-) diff --git a/micro_manager/adaptivity/adaptivity.py b/micro_manager/adaptivity/adaptivity.py index e00bae9e..4a3905b0 100644 --- a/micro_manager/adaptivity/adaptivity.py +++ b/micro_manager/adaptivity/adaptivity.py @@ -27,18 +27,21 @@ def __init__(self, configurator, rank, nsims) -> None: nsims : int Number of micro simulations. """ - self._refine_const_input = configurator.get_adaptivity_refining_const() - self._refine_const = self._refine_const_input - self._adaptive_refine_const = configurator.get_adaptivity_for_refining_const() + self._refine_const = configurator.get_adaptivity_refining_const() self._coarse_const = configurator.get_adaptivity_coarsening_const() self._hist_param = configurator.get_adaptivity_hist_param() self._adaptivity_data_names = configurator.get_data_for_adaptivity() self._adaptivity_type = configurator.get_adaptivity_type() - self._config_file_name = configurator.get_config_file_name() + self._adaptivity_output_type = configurator.get_adaptivity_output_type() + + self._dynamic_adaptivity = configurator.get_dynamic_adaptivity() + self._dynamic_refine_const = self._refine_const + self._precice_config_file_name = configurator.get_precice_config_file_name() self._convergence_measure = [] - self._convergence_status = [] self._min_addition = 1.0 - self._adaptivity_output_type = configurator.get_adaptivity_output_type() + self._logger = Logger( + "adaptivity-logger", "adaptivity-" + str(rank) + ".log", rank + ) self._micro_problem = getattr( importlib.import_module( @@ -103,8 +106,29 @@ def __init__(self, configurator, rank, nsims) -> None: csv_logger=True, ) - with open(self._config_file_name, "r") as xml_file: - self.xml_data = xml_file.read() + if self._dynamic_adaptivity: + # Read convergence measures from preCICE configuration file + self._data_values, self._limit_values = self.read_convergence_measures() + + def read_convergence_measures(self): + """ + Reads convergence measures from a preCICE configuration file. + + Parameters: + ----------- + config_file_name : str + Path to the preCICE configuration file. + + Returns: + -------- + data_values : list + List of data names involved in the convergence measurement + limit_values : list + List of limit attributes for corresponding data names + """ + # Read the XML configuration file + with open(self._precice_config_file_name, "r") as xml_file: + xml_data = xml_file.read() unique_names = [ "absolute-convergence-measure", @@ -113,32 +137,28 @@ def __init__(self, configurator, rank, nsims) -> None: ] # Initialize lists to store the found attributes - self.data_values = [] - self.limit_values = [] + data_values = [] + limit_values = [] for unique_name in unique_names: pattern = f'<{unique_name} limit="([^"]+)" data="([^"]+)" mesh="([^"]+)"' - matches = re.finditer(pattern, self.xml_data) + matches = re.finditer(pattern, xml_data) for match in matches: - self.data_values.append(match.group(2)) - self.limit_values.append(match.group(1)) + data_values.append(match.group(2)) + limit_values.append(match.group(1)) # Check if any matches were found - if self.data_values and self.limit_values: + if data_values and limit_values: for i, (data_value, limit_value) in enumerate( - zip(self.data_values, self.limit_values), start=1 + zip(data_values, limit_values), start=1 ): print(f"Match {i}:") print(f"Data: {data_value}") print(f"Limit: {limit_value}") else: - print(f"No attributes found for unique name '{unique_name}'") - - def get_data_values(self): - return self.data_values + print(f"No attributes found for unique names: {unique_names}") - def get_limit_values(self): - return self.limit_values + return data_values, limit_values def _update_similarity_dists(self, dt: float, data: dict) -> None: """ @@ -164,7 +184,7 @@ def _update_similarity_dists(self, dt: float, data: dict) -> None: self._similarity_dists += dt * self._similarity_measure(data_vals) - def _get_addition(self, similarity_const: float) -> float: + def _get_addition(self) -> float: """ Get adapted refining constant based on limit values in preCICE configuration file and convergence measurements in preCICE @@ -175,8 +195,7 @@ def _get_addition(self, similarity_const: float) -> float: # read convergence value from precice-Mysolver-convergence.log file convergence_values = [] # last iteration - # convergence_rate = 1.0 - additional = 0.0 + addition = 0.0 file_path = None file_name_suffix = "-convergence.log" @@ -188,76 +207,97 @@ def _get_addition(self, similarity_const: float) -> float: break with open(file_path, "r") as file: lines = file.readlines() - if len(lines) < 1: - print("File is empty.") - else: - if len(lines) > len(self._convergence_measure): - if len(lines) == 2: - self._convergence_measure.append(lines[0].strip().split()) - self._convergence_measure.append(lines[-1].strip().split()) - header_line = self._convergence_measure[0] - last_line = self._convergence_measure[-1] - for data in self.data_values: - for element in header_line: - if data in element: - index = header_line.index(element) - if last_line[index] == "inf": - convergence_values.append(1e20) - else: - index_config = self.data_values.index(data) - convergence_values.append( - max( - float(last_line[index]), - float(self.limit_values[index_config]), - ) - ) - min_convergence = np.log10( - np.prod( - np.array(self.limit_values, dtype=float) - / np.array(convergence_values, dtype=float) - ) - ) - if last_line[1] == "60": - min_convergence = max(0.0, min_convergence) - self._convergence_status.append(min_convergence) - alpha = 3.0 + if len(lines) > 1 and len(lines) > len(self._convergence_measure): + if len(lines) == 2: + self._convergence_measure.append(lines[0].strip().split()) + self._convergence_measure.append(lines[-1].strip().split()) + header_line = self._convergence_measure[0] + last_line = self._convergence_measure[-1] - if len(self._convergence_status) <= 2: - self._min_addition = 1.0 - self._logger.info("less than two iterations, relax") - self._logger.info( - "min Convergence: {} ".format(self._convergence_status[-1]) - ) - additional = ( - 1 + 1.0 / (min(0.0, self._convergence_status[-1]) - 1.0) - ) ** alpha + if int(last_line[0]) == 1: + self._logger.log_info("first time window") + addition = 0.0 + else: + if int(last_line[1]) == 1: + self._min_addition = 1.0 + else: + if self._min_addition == 0.0: + addition = 0.0 + else: + for data in self._data_values: + for element in header_line: + if data in element: + index = header_line.index(element) + if last_line[index] == "inf": + convergence_values.append(1e20) + else: + index_config = self._data_values.index(data) + convergence_values.append( + max( + float(last_line[index]), + float(self._limit_values[index_config]), + ) + ) + min_convergence = np.log10( + np.prod( + np.array(self._limit_values, dtype=float) + / np.array(convergence_values, dtype=float) + ) + ) + + self._logger.log_info( + "min Convergence: {} ".format(min_convergence) + ) + + alpha = 3.0 + addition = min( + self._min_addition, min( + (1 + 1.0 / (min(0.0, min_convergence) - 1.0)) ** alpha, float(last_line[2])/self._max_similarity_dist/self._coarse_const) + ) + self._min_addition = addition + + return addition + + def _get_dynamic_adaptivity_refine_const(self) -> float: + """ + Get dynamic adaptivity refine constant. - return additional + Returns + ------- + dynamic_adaptivity_refine_const : float + Dynamic adaptivity refine constant. + """ + return self._dynamic_refine_const - def _get_adaptive_refining_const(self) -> float: - return self._refine_const + def _update_active_sims(self, is_sim_active, just_deactivated) -> None: + """ + Update set of active micro simulations. + """ + self._is_sim_active = is_sim_active + self._just_deactivated = just_deactivated - def _update_active_sims(self, use_dyn_ref_tol: bool = False) -> None: + def _compute_active_sims(self, use_dyn) -> tuple: """ - Update set of active micro simulations. Active micro simulations are compared to each other + Campute the set of active micro simulations. Active micro simulations are compared to each other and if found similar, one of them is deactivated. """ - if use_dyn_ref_tol and self._adaptive_refine_const: - additional = self._get_addition(self._refine_const_input) * ( - 1 - self._refine_const_input - ) - self._min_addition = min(self._min_addition, additional) - additional = self._min_addition - if additional > 0.0: - _refine_const = additional + self._refine_const_input + is_sim_active = self._is_sim_active.copy() + just_deactivated = self._just_deactivated.copy() + + if use_dyn and self._dynamic_adaptivity: + addition = self._get_addition() * (1 - self._refine_const) + # self._min_addition = min(self._min_addition, addition) + # addition = self._min_addition + if addition > 0.0: + self._dynamic_refine_const = addition + self._refine_const else: - _refine_const = self._refine_const_input - self._logger.info("Adaptive refine constant: {}".format(self._refine_const)) + self._dynamic_refine_const = self._refine_const + self._logger.log_info( + "Adaptive refine constant: {}".format(self._dynamic_refine_const) + ) else: - _refine_const = self._refine_const_input - - max_similarity_dist = np.amax(similarity_dists) + self._dynamic_refine_const = self._refine_const if self._max_similarity_dist == 0.0: warn( @@ -265,14 +305,20 @@ def _update_active_sims(self, use_dyn_ref_tol: bool = False) -> None: ) self._coarse_tol = sys.float_info.min else: - self._coarse_tol = self._coarse_const * _refine_const * max_similarity_dist + self._coarse_tol = ( + self._coarse_const + * self._dynamic_refine_const + * self._max_similarity_dist + ) + self._logger.log_info("Coarsening tolerance: {}".format(self._coarse_tol)) # Update the set of active micro sims for i in range(self._is_sim_active.size): - if self._is_sim_active[i]: # if sim is active - if self._check_for_deactivation(i, self._is_sim_active): - self._is_sim_active[i] = False - self._just_deactivated.append(i) + if is_sim_active[i]: # if sim is active + if self._check_for_deactivation(i, is_sim_active): + is_sim_active[i] = False + just_deactivated.append(i) + return is_sim_active, just_deactivated def _associate_inactive_to_active(self) -> None: """ diff --git a/micro_manager/adaptivity/global_adaptivity.py b/micro_manager/adaptivity/global_adaptivity.py index 74e3052f..ab1f6f89 100644 --- a/micro_manager/adaptivity/global_adaptivity.py +++ b/micro_manager/adaptivity/global_adaptivity.py @@ -151,51 +151,60 @@ def compute_adaptivity( ): # Only the first rank in the node updates the similarity distances self._update_similarity_dists(dt, global_data_for_adaptivity) - is_sim_active_dyn, refine_const_dyn = self._update_active_sims( - similarity_dists, is_sim_active_nm1, True - ) - is_sim_active_dyn, sim_is_associated_to_dyn = self._update_inactive_sims( - similarity_dists, - is_sim_active_dyn, - sim_is_associated_to_nm1, - micro_sims, - refine_const_dyn, - ) + self._comm_node.Barrier() # Wait for the similarity distances to be updated on all ranks of the node - is_sim_active_sta, refine_const_sta = self._update_active_sims( - similarity_dists, is_sim_active_nm1, False - ) - is_sim_active_sta, sim_is_associated_to_sta = self._update_inactive_sims( - similarity_dists, - is_sim_active_sta, - sim_is_associated_to_nm1, - micro_sims, - refine_const_sta, + self._max_similarity_dist = np.amax(self._similarity_dists) + + _is_sim_active_sta, _just_deactivated_sta = self._compute_active_sims(False) + ( + _is_sim_active_sta, + _sim_is_associated_to_sta, + _to_be_activated_ids_sta, + ) = self._compute_inactive_sims( + self._refine_const, _is_sim_active_sta, _just_deactivated_sta ) - if np.array_equal(is_sim_active_dyn, is_sim_active_sta) and np.array_equal( - sim_is_associated_to_dyn, sim_is_associated_to_sta - ): - is_sim_active = is_sim_active_sta - sim_is_associated_to = sim_is_associated_to_sta - self._refine_const = refine_const_sta - else: - is_sim_active = is_sim_active_dyn - sim_is_associated_to = sim_is_associated_to_dyn - self._refine_const = refine_const_dyn + self._logger.log_info( + "active sims (static): {}".format(np.sum(_is_sim_active_sta)) + ) - self._comm_node.Barrier() # Wait for the similarity distances to be updated on all ranks of the node + _is_sim_active_dyn, _just_deactivated_dyn = self._compute_active_sims(True) + ( + _is_sim_active_dyn, + _sim_is_associated_to_dyn, + _to_be_activated_ids_dyn, + ) = self._compute_inactive_sims( + self._dynamic_refine_const, _is_sim_active_dyn, _just_deactivated_dyn + ) - self._max_similarity_dist = np.amax(self._similarity_dists) + self._logger.log_info( + "active sims (dynamic): {}".format(np.sum(_is_sim_active_dyn)) + ) - self._update_active_sims() + self._update_active_sims(_is_sim_active_dyn, _just_deactivated_dyn) - self._update_inactive_sims(micro_sims) + self._update_inactive_sims( + micro_sims, + _is_sim_active_dyn, + _sim_is_associated_to_dyn, + _to_be_activated_ids_dyn, + ) self._associate_inactive_to_active() self._precice_participant.stop_last_profiling_section() + if np.array_equal(_is_sim_active_dyn, _is_sim_active_sta) and np.array_equal( + _sim_is_associated_to_dyn, _sim_is_associated_to_sta + ): + self._dynamic_refine_const = self._refine_const + + self._logger.log_info( + "send refine const {} ---------------------------".format( + self._dynamic_refine_const + ) + ) + def get_active_sim_ids(self) -> np.ndarray: """ Get the ids of active simulations. @@ -373,33 +382,94 @@ def _communicate_micro_output( for local_id in active_to_inactive_map[assoc_active_ids[count]]: micro_output[local_id] = deepcopy(output) - def _update_inactive_sims(self, micro_sims: list) -> None: + def interpolate_inactive_outputs(micro_output, is_sim_active, simulation_positions): """ - Update set of inactive micro simulations. Each inactive micro simulation is compared to all active ones and if it is not similar to any of them, it is activated. - - If a micro simulation which has been inactive since the start of the simulation is activated for the - first time, the simulation object is created and initialized. - - Parameters - ---------- - micro_sims : list - List of objects of class MicroProblem, which are the micro simulations + Interpolates the output for inactive simulations based on the closest two active simulations. + + Parameters: + ----------- + micro_output : dict + Dictionary containing the outputs of all simulations. + is_sim_active : np.ndarray + Boolean array indicating which simulations are active. + simulation_positions : np.ndarray + Array of positions (or coordinates) for each simulation. + + Returns: + -------- + micro_output : dict + Updated dictionary with interpolated outputs for inactive simulations. """ - self._ref_tol = refine_const * self._max_similarity_dist + num_simulations = len(is_sim_active) + + for local_id in range(num_simulations): + if not is_sim_active[local_id]: # If the simulation is inactive + # Compute distances to all active simulations + distances = np.linalg.norm(simulation_positions[is_sim_active] - simulation_positions[local_id], axis=1) + + # Find indices of the two closest active simulations + closest_indices = np.argsort(distances)[:2] + closest_active_ids = np.where(is_sim_active)[0][closest_indices] + + # Get the outputs and distances of the two closest active simulations + output_1 = micro_output[closest_active_ids[0]] + output_2 = micro_output[closest_active_ids[1]] + distance_1 = distances[closest_indices[0]] + distance_2 = distances[closest_indices[1]] + + # Perform weighted interpolation + total_distance = distance_1 + distance_2 + weight_1 = 1 - (distance_1 / total_distance) + weight_2 = 1 - (distance_2 / total_distance) + + # Interpolate the output + interpolated_output = { + key: weight_1 * output_1[key] + weight_2 * output_2[key] + for key in output_1.keys() + } + + # Assign the interpolated output to the inactive simulation + micro_output[local_id] = interpolated_output + + def _compute_inactive_sims( + self, _refine_const, is_sim_active, just_deactivated + ) -> tuple: + self._ref_tol = _refine_const * self._max_similarity_dist _sim_is_associated_to_updated = np.copy(self._sim_is_associated_to) # Check inactive simulations for activation and collect IDs of those to be activated to_be_activated_ids = [] # Global IDs to be activated - for i in range(self._is_sim_active.size): - if not self._is_sim_active[i]: # if id is inactive - if self._check_for_activation(i, self._is_sim_active): - self._is_sim_active[i] = True + for i in range(is_sim_active.size): + if not is_sim_active[i]: # if id is inactive + if self._check_for_activation(i, is_sim_active): + is_sim_active[i] = True _sim_is_associated_to_updated[ i ] = -2 # Active sim cannot have an associated sim - if self._is_sim_on_this_rank[i] and i not in self._just_deactivated: + if self._is_sim_on_this_rank[i] and i not in just_deactivated: to_be_activated_ids.append(i) + return is_sim_active, _sim_is_associated_to_updated, to_be_activated_ids + + def _update_inactive_sims( + self, + micro_sims: list, + _is_sim_active, + _sim_is_associated_to_updated, + to_be_activated_ids, + ) -> None: + """ + Update set of inactive micro simulations. Each inactive micro simulation is compared to all active ones and if it is not similar to any of them, it is activated. + + If a micro simulation which has been inactive since the start of the simulation is activated for the + first time, the simulation object is created and initialized. + + Parameters + ---------- + micro_sims : list + List of objects of class MicroProblem, which are the micro simulations + """ + self._is_sim_active = np.copy(_is_sim_active) self._just_deactivated.clear() # Clear the list of sims deactivated in this step diff --git a/micro_manager/adaptivity/local_adaptivity.py b/micro_manager/adaptivity/local_adaptivity.py index 99ec3f87..cdd3b057 100644 --- a/micro_manager/adaptivity/local_adaptivity.py +++ b/micro_manager/adaptivity/local_adaptivity.py @@ -77,54 +77,39 @@ def compute_adaptivity( self._update_similarity_dists(dt, data_for_adaptivity) - # Operation done globally if global adaptivity is chosen - is_sim_active_dyn, refine_const_dyn = self._update_active_sims( - similarity_dists, is_sim_active_nm1, True - ) - is_sim_active_dyn, sim_is_associated_to_dyn = self._update_inactive_sims( - similarity_dists, - is_sim_active_dyn, - sim_is_associated_to_nm1, - micro_sims, - refine_const_dyn, - ) + self._max_similarity_dist = np.amax(self._similarity_dists) - is_sim_active_sta, refine_const_sta = self._update_active_sims( - similarity_dists, is_sim_active_nm1, False - ) - is_sim_active_sta, sim_is_associated_to_sta = self._update_inactive_sims( - similarity_dists, - is_sim_active_sta, - sim_is_associated_to_nm1, - micro_sims, + _is_sim_active_sta, _just_deactivated_sta = self._compute_active_sims(False) + ( + _is_sim_active_sta, + _sim_is_associated_to_sta, refine_const_sta, - ) - - if np.array_equal(is_sim_active_dyn, is_sim_active_sta) and np.array_equal( - sim_is_associated_to_dyn, sim_is_associated_to_sta - ): - is_sim_active = is_sim_active_sta - sim_is_associated_to = sim_is_associated_to_sta - self._refine_const = refine_const_sta - else: - is_sim_active = is_sim_active_dyn - sim_is_associated_to = sim_is_associated_to_dyn - self._refine_const = refine_const_dyn - - sim_is_associated_to = self._associate_inactive_to_active( - similarity_dists, is_sim_active, sim_is_associated_to - ) - - self._max_similarity_dist = np.amax(self._similarity_dists) + ) = self._compute_inactive_sims(self._refine_const) + _is_sim_active_dyn, _just_deactivated_dyn = self._compute_active_sims(True) + ( + _is_sim_active_dyn, + _sim_is_associated_to_dyn, + _to_be_activated_ids_dyn, + ) = self._compute_inactive_sims(self._dynamic_refine_const) - self._update_active_sims() + self._update_active_sims(_is_sim_active_dyn, _just_deactivated_dyn) - self._update_inactive_sims(micro_sims) + self._update_inactive_sims( + micro_sims, + _is_sim_active_dyn, + _sim_is_associated_to_dyn, + _to_be_activated_ids_dyn, + ) self._associate_inactive_to_active() self._precice_participant.stop_last_profiling_section() + if np.array_equal(_is_sim_active_dyn, _is_sim_active_sta) and np.array_equal( + _sim_is_associated_to_dyn, _sim_is_associated_to_sta + ): + self._dynamic_refine_const = self._refine_const + def get_active_sim_ids(self) -> np.ndarray: """ Get the ids of active simulations. @@ -236,7 +221,26 @@ def log_metrics(self, n: int) -> None: ) ) - def _update_inactive_sims(self, micro_sims: list) -> None: + def _compute_inactive_sims(self, _refine_const) -> tuple: + self._ref_tol = _refine_const * self._max_similarity_dist + + _sim_is_associated_to_updated = np.copy(self._sim_is_associated_to) + is_sim_active = np.copy(self._is_sim_active) + + # Check inactive simulations for activation and collect IDs of those to be activated + to_be_activated_ids = [] # Global IDs to be activated + for i in range(is_sim_active.size): + if not is_sim_active[i]: # if id is inactive + if self._check_for_activation(i, is_sim_active): + is_sim_active[i] = True + _sim_is_associated_to_updated[ + i + ] = -2 # Active sim cannot have an associated sim + if self._is_sim_on_this_rank[i] and i not in self._just_deactivated: + to_be_activated_ids.append(i) + return is_sim_active, _sim_is_associated_to_updated, to_be_activated_ids + + def _update_inactive_sims(self, micro_sims: list, _is_sim_active) -> None: """ Update set of inactive micro simulations. Each inactive micro simulation is compared to all active ones and if it is not similar to any of them, it is activated. @@ -249,33 +253,10 @@ def _update_inactive_sims(self, micro_sims: list) -> None: micro_sims : list List containing micro simulation objects. """ - self._ref_tol = refine_const * self._max_similarity_dist - - _is_sim_active = np.copy( - is_sim_active - ) # Input is_sim_active is not longer used after this point - _sim_is_associated_to = np.copy(sim_is_associated_to) - - to_be_activated_ids = [] - # Update the set of inactive micro sims - for i in range(self._is_sim_active.size): - if not self._is_sim_active[i]: # if id is inactive - if self._check_for_activation(i, self._is_sim_active): - self._is_sim_active[i] = True - if i not in self._just_deactivated: - to_be_activated_ids.append(i) + self._is_sim_active = np.copy(_is_sim_active) self._just_deactivated.clear() # Clear the list of sims deactivated in this step - # Update the set of inactive micro sims - for i in to_be_activated_ids: - associated_active_id = self._sim_is_associated_to[i] - micro_sims[i] = create_simulation_class(self._micro_problem)(i) - micro_sims[i].set_state(micro_sims[associated_active_id].get_state()) - self._sim_is_associated_to[ - i - ] = -2 # Active sim cannot have an associated sim - # Delete the inactive micro simulations which have not been activated for i in range(self._is_sim_active.size): if not self._is_sim_active[i]: diff --git a/micro_manager/config.py b/micro_manager/config.py index b8904177..5a20600a 100644 --- a/micro_manager/config.py +++ b/micro_manager/config.py @@ -51,7 +51,7 @@ def __init__(self, config_file_name): self._adaptivity_coarsening_constant = 0.5 self._adaptivity_refining_constant = 0.5 self._adaptivity_every_implicit_iteration = False - self._adaptivity_for_refining_constant = False + self._dynamic_adaptivity = False self._adaptivity_similarity_measure = "L1" self._adaptivity_output_type = "" self._adaptivity_output_n = 1 @@ -375,18 +375,21 @@ def read_json_micro_manager(self): "Micro Manager will compute adaptivity in every implicit iteration, if implicit coupling is done." ) - adaptivity_for_refining_constant = self._data["simulation_params"][ - "adaptivity_settings" - ]["adaptive_refining_constant"] - if adaptivity_for_refining_constant: - self._adaptivity_for_refining_constant = True - elif adaptivity_for_refining_constant: - self._adaptivity_for_refining_constant = False - self._logger.info( - "The adaptivity for refining constant is {}.".format( - self._adaptivity_for_refining_constant + try: + adaptivity_for_refining_constant = self._data[ + "simulation_params" + ]["adaptivity_settings"]["adaptive_refining_constant"] + if adaptivity_for_refining_constant: + self._dynamic_adaptivity = True + self._logger.log_info_rank_zero( + "The adaptivity for refining constant is {}.".format( + self._dynamic_adaptivity + ) + ) + except: + self._logger.log_info_rank_zero( + "No dynamic adaptivity for refining constant provided, defaulting to False." ) - ) elif not adaptivity_every_implicit_iteration: self._adaptivity_every_implicit_iteration = False self._logger.log_info_rank_zero( @@ -693,7 +696,7 @@ def get_adaptivity_refining_const(self): """ return self._adaptivity_refining_constant - def get_adaptivity_for_refining_const(self): + def get_dynamic_adaptivity(self): """ Get adaptivity for refining constant. More details: https://precice.org/tooling-micro-manager-configuration.html#adaptivity @@ -703,7 +706,7 @@ def get_adaptivity_for_refining_const(self): adaptivity_for_refining_constant : bool Adaptivity for refining constant """ - return self._adaptivity_for_refining_constant + return self._dynamic_adaptivity def get_adaptivity_similarity_measure(self): """ diff --git a/micro_manager/micro_manager.py b/micro_manager/micro_manager.py index b4eb1276..c4a8881f 100644 --- a/micro_manager/micro_manager.py +++ b/micro_manager/micro_manager.py @@ -913,10 +913,7 @@ def _solve_micro_simulations_with_adaptivity( self._data_for_adaptivity[name][i] = micro_sims_output[i][name] # Add similarity constants to the output - ref_const = self._adaptivity_controller._get_adaptive_refining_const() - self._logger.info( - "getting adaptivity constants to send to macro {}".format(ref_const) - ) + ref_const = self._adaptivity_controller._get_dynamic_adaptivity_refine_const() for inactive_id in inactive_sim_ids: micro_sims_output[inactive_id]["refine_const"] = ref_const for active_id in active_sim_ids: From 74d1f51eba79b5e5167742ba4d7b9197957b0861 Mon Sep 17 00:00:00 2001 From: Jun Chen Date: Fri, 15 Aug 2025 11:28:40 +0200 Subject: [PATCH 08/11] remove experimental func --- micro_manager/adaptivity/global_adaptivity.py | 49 ------------------- 1 file changed, 49 deletions(-) diff --git a/micro_manager/adaptivity/global_adaptivity.py b/micro_manager/adaptivity/global_adaptivity.py index ab1f6f89..a1d3225a 100644 --- a/micro_manager/adaptivity/global_adaptivity.py +++ b/micro_manager/adaptivity/global_adaptivity.py @@ -382,55 +382,6 @@ def _communicate_micro_output( for local_id in active_to_inactive_map[assoc_active_ids[count]]: micro_output[local_id] = deepcopy(output) - def interpolate_inactive_outputs(micro_output, is_sim_active, simulation_positions): - """ - Interpolates the output for inactive simulations based on the closest two active simulations. - - Parameters: - ----------- - micro_output : dict - Dictionary containing the outputs of all simulations. - is_sim_active : np.ndarray - Boolean array indicating which simulations are active. - simulation_positions : np.ndarray - Array of positions (or coordinates) for each simulation. - - Returns: - -------- - micro_output : dict - Updated dictionary with interpolated outputs for inactive simulations. - """ - num_simulations = len(is_sim_active) - - for local_id in range(num_simulations): - if not is_sim_active[local_id]: # If the simulation is inactive - # Compute distances to all active simulations - distances = np.linalg.norm(simulation_positions[is_sim_active] - simulation_positions[local_id], axis=1) - - # Find indices of the two closest active simulations - closest_indices = np.argsort(distances)[:2] - closest_active_ids = np.where(is_sim_active)[0][closest_indices] - - # Get the outputs and distances of the two closest active simulations - output_1 = micro_output[closest_active_ids[0]] - output_2 = micro_output[closest_active_ids[1]] - distance_1 = distances[closest_indices[0]] - distance_2 = distances[closest_indices[1]] - - # Perform weighted interpolation - total_distance = distance_1 + distance_2 - weight_1 = 1 - (distance_1 / total_distance) - weight_2 = 1 - (distance_2 / total_distance) - - # Interpolate the output - interpolated_output = { - key: weight_1 * output_1[key] + weight_2 * output_2[key] - for key in output_1.keys() - } - - # Assign the interpolated output to the inactive simulation - micro_output[local_id] = interpolated_output - def _compute_inactive_sims( self, _refine_const, is_sim_active, just_deactivated ) -> tuple: From 1b7070aa8110870ce2308025ae53da0d6b09a74a Mon Sep 17 00:00:00 2001 From: Jun Chen Date: Mon, 10 Nov 2025 16:19:36 +0100 Subject: [PATCH 09/11] add interpolation and history-state reusing --- micro_manager/adaptivity/adaptivity.py | 102 ++++++++++++-- micro_manager/adaptivity/global_adaptivity.py | 132 +++++++++++++++--- micro_manager/config.py | 25 ++++ micro_manager/micro_manager.py | 21 ++- 4 files changed, 244 insertions(+), 36 deletions(-) diff --git a/micro_manager/adaptivity/adaptivity.py b/micro_manager/adaptivity/adaptivity.py index 4a3905b0..e35592ab 100644 --- a/micro_manager/adaptivity/adaptivity.py +++ b/micro_manager/adaptivity/adaptivity.py @@ -34,6 +34,7 @@ def __init__(self, configurator, rank, nsims) -> None: self._adaptivity_type = configurator.get_adaptivity_type() self._adaptivity_output_type = configurator.get_adaptivity_output_type() + self._use_interpolation_adaptivity = configurator.get_interpolation() self._dynamic_adaptivity = configurator.get_dynamic_adaptivity() self._dynamic_refine_const = self._refine_const self._precice_config_file_name = configurator.get_precice_config_file_name() @@ -61,13 +62,21 @@ def __init__(self, configurator, rank, nsims) -> None: # Start adaptivity calculation with all sims active # This array is modified in place via the function update_active_sims and update_inactive_sims self._is_sim_active = np.array([True] * nsims, dtype=np.bool_) + self._is_sim_active_ref = self._is_sim_active.copy() + self._is_sim_to_solve = self._is_sim_active.copy() + self._gobal_data_last = None + self._hist_dists = np.zeros((nsims), dtype=float) # sim_is_associated_to: 1D array with values of associated simulations of inactive simulations. Active simulations have None # Active sims do not have an associated sim # This array is modified in place via the function associate_inactive_to_active - self._sim_is_associated_to = np.full((nsims), -2, dtype=np.intc) + self._sim_is_associated_to = np.full((nsims, 2), -2, dtype=np.intc) + self._sim_association_dists = np.full( + (len(self._is_sim_active), 2), 0, dtype=float + ) self._just_deactivated: list[int] = [] + self._just_deactivated_ref: list[int] = [] self._similarity_measure = self._get_similarity_measure( configurator.get_adaptivity_similarity_measure() @@ -184,6 +193,33 @@ def _update_similarity_dists(self, dt: float, data: dict) -> None: self._similarity_dists += dt * self._similarity_measure(data_vals) + def _update_history_dists(self, data_last: dict, data: dict) -> None: + """ + Calculate metric which determines if one micro simulations is similar enough to the history. + + Parameters + ---------- + data_last : dict + Data from last time step to be used in similarity distance calculation + data : dict + Data to be used in similarity distance calculation + """ + if data_last is None: + self._hist_dists = np.zeros((len(self._is_sim_active)), dtype=float) + return + + for name in data.keys(): + data_vals = np.array(data[name]) + data_last_vals = np.array(data_last[name]) + self._hist_dists += np.abs(data_vals - data_last_vals) + + def _reset_hist(self) -> None: + """ + Reset history distances to zero. + """ + self._hist_dists = np.zeros((len(self._is_sim_active)), dtype=float) + self._gobal_data_last = None + def _get_addition(self) -> float: """ Get adapted refining constant based on limit values in preCICE configuration file and convergence measurements in preCICE @@ -252,8 +288,13 @@ def _get_addition(self) -> float: alpha = 3.0 addition = min( - self._min_addition, min( - (1 + 1.0 / (min(0.0, min_convergence) - 1.0)) ** alpha, float(last_line[2])/self._max_similarity_dist/self._coarse_const) + self._min_addition, + min( + (1 + 1.0 / (min(0.0, min_convergence) - 1.0)) ** alpha, + float(last_line[2]) + / self._max_similarity_dist + / self._coarse_const, + ), ) self._min_addition = addition @@ -276,13 +317,22 @@ def _update_active_sims(self, is_sim_active, just_deactivated) -> None: """ self._is_sim_active = is_sim_active self._just_deactivated = just_deactivated + self._is_sim_to_solve = self._is_sim_active.copy() + self._is_sim_active_ref = self._is_sim_active.copy() + + def _update_active_sims_ref(self, is_sim_active_ref, just_deactivated_ref) -> None: + """ + Update set of active micro simulations. + """ + self._is_sim_active_ref = is_sim_active_ref + self._just_deactivated_ref = just_deactivated_ref def _compute_active_sims(self, use_dyn) -> tuple: """ Campute the set of active micro simulations. Active micro simulations are compared to each other and if found similar, one of them is deactivated. """ - is_sim_active = self._is_sim_active.copy() + is_sim_active = self._is_sim_active_ref.copy() just_deactivated = self._just_deactivated.copy() if use_dyn and self._dynamic_adaptivity: @@ -320,6 +370,19 @@ def _compute_active_sims(self, use_dyn) -> tuple: just_deactivated.append(i) return is_sim_active, just_deactivated + def _compute_sims_to_solve(self) -> None: + """ + Compute which simulations to solve. Simulations to solve are active simulations and inactive simulations which were just deactivated. + """ + Cr = self._dynamic_refine_const + tol_u = max(self._hist_dists[self._is_sim_to_solve]) * Cr + if sum(self._is_sim_active) > 1: + for i in range(self._is_sim_active.size): + if self._is_sim_active[i] and self._hist_dists[i] >= tol_u: + self._hist_dists[i] = 0.0 + else: + self._is_sim_to_solve[i] = False + def _associate_inactive_to_active(self) -> None: """ Associate inactive micro simulations to most similar active micro simulation. @@ -331,17 +394,30 @@ def _associate_inactive_to_active(self) -> None: # Add the +1 for the case when the similarity distance matrix is zeros dist_min_start_value = self._max_similarity_dist + 1 - # Associate inactive micro sims to active micro sims + # Associate inactive micro sims to the two most similar active micro sims for inactive_id in inactive_ids: - # Begin with a large distance to trigger the search for the most similar active sim - dist_min = dist_min_start_value - for active_id in active_ids: - # Find most similar active sim for every inactive sim - if self._similarity_dists[inactive_id, active_id] < dist_min: - associated_active_id = active_id - dist_min = self._similarity_dists[inactive_id, active_id] + # Initialize distances and associated IDs + closest_active_ids = [len(self._is_sim_active) - 1, self._is_sim_active - 1] + closest_dists = [dist_min_start_value, dist_min_start_value] - self._sim_is_associated_to[inactive_id] = associated_active_id + for active_id in active_ids: + # Get the similarity distance + dist = self._similarity_dists[inactive_id, active_id] + + # Update the closest and second-closest active simulations + if dist < closest_dists[0]: # Found a new closest simulation + closest_dists[1] = closest_dists[0] + closest_active_ids[1] = closest_active_ids[0] + + closest_dists[0] = dist + closest_active_ids[0] = active_id + elif dist < closest_dists[1]: # Found a new second-closest simulation + closest_dists[1] = dist + closest_active_ids[1] = active_id + + # Store the two closest active simulations and their distances + self._sim_is_associated_to[inactive_id] = closest_active_ids + self._sim_association_dists[inactive_id] = closest_dists def _check_for_activation( self, inactive_id: int, is_sim_active: np.ndarray diff --git a/micro_manager/adaptivity/global_adaptivity.py b/micro_manager/adaptivity/global_adaptivity.py index a1d3225a..86564ecd 100644 --- a/micro_manager/adaptivity/global_adaptivity.py +++ b/micro_manager/adaptivity/global_adaptivity.py @@ -150,6 +150,10 @@ def compute_adaptivity( self._MPI_local_rank == 0 ): # Only the first rank in the node updates the similarity distances self._update_similarity_dists(dt, global_data_for_adaptivity) + self._update_history_dists( + self._gobal_data_last, global_data_for_adaptivity + ) + self._gobal_data_last = deepcopy(global_data_for_adaptivity) self._comm_node.Barrier() # Wait for the similarity distances to be updated on all ranks of the node @@ -164,6 +168,8 @@ def compute_adaptivity( self._refine_const, _is_sim_active_sta, _just_deactivated_sta ) + self._update_active_sims_ref(_is_sim_active_sta, _just_deactivated_sta) + self._logger.log_info( "active sims (static): {}".format(np.sum(_is_sim_active_sta)) ) @@ -187,8 +193,10 @@ def compute_adaptivity( micro_sims, _is_sim_active_dyn, _sim_is_associated_to_dyn, - _to_be_activated_ids_dyn, + _to_be_activated_ids_sta, ) + self._compute_sims_to_solve() + self._logger.log_info("sims to solve: {}".format(np.sum(self._is_sim_to_solve))) self._associate_inactive_to_active() @@ -218,6 +226,19 @@ def get_active_sim_ids(self) -> np.ndarray: self._is_sim_active[self._global_ids[0] : self._global_ids[-1] + 1] )[0] + def get_active_sim_ids_to_solve(self) -> np.ndarray: + """ + Get the ids of active simulations. + + Returns + ------- + numpy array + 1D array of active simulation ids + """ + return np.where( + self._is_sim_to_solve[self._global_ids[0] : self._global_ids[-1] + 1] + )[0] + def get_inactive_sim_ids(self) -> np.ndarray: """ Get the ids of inactive simulations. @@ -296,7 +317,7 @@ def log_metrics(self, n: int) -> None: for global_id in self._global_ids: if not self._is_sim_active[global_id]: assoc_rank = int( - ranks_of_sims[self._sim_is_associated_to[global_id]] + ranks_of_sims[self._sim_is_associated_to[global_id][0]] ) if not assoc_rank in assoc_ranks: assoc_ranks.append(assoc_rank) @@ -353,6 +374,9 @@ def _communicate_micro_output( local_sim_is_associated_to = self._sim_is_associated_to[ self._global_ids[0] : self._global_ids[-1] + 1 ] + local_sim_association_dists = self._sim_association_dists[ + self._global_ids[0] : self._global_ids[-1] + 1 + ] # Keys are global IDs of active simulations associated to inactive # simulations on this rank. Values are global IDs of the inactive @@ -360,17 +384,49 @@ def _communicate_micro_output( active_to_inactive_map: Dict[int, list] = dict() for i in inactive_local_ids: - assoc_active_id = local_sim_is_associated_to[i] - # Gather global IDs of associated active simulations not on this rank - if not self._is_sim_on_this_rank[assoc_active_id]: - if assoc_active_id in active_to_inactive_map: - active_to_inactive_map[assoc_active_id].append(i) - else: - active_to_inactive_map[assoc_active_id] = [i] - else: # If associated active simulation is on this rank, copy the output directly + assoc_active_ids = local_sim_is_associated_to[i] + assoc_dists = local_sim_association_dists[i] + + # # Gather global IDs of associated active simulations not on this rank + # if not all(self._is_sim_on_this_rank[assoc_active_ids]): + # for assoc_active_id in assoc_active_ids: + # if not self._is_sim_on_this_rank[assoc_active_id]: + # if assoc_active_id in active_to_inactive_map: + # active_to_inactive_map[assoc_active_id].append(i) + # else: + # active_to_inactive_map[assoc_active_id] = [i] + # else: # If associated active simulations are on this rank, interpolate the output + if assoc_active_ids[0] == assoc_active_ids[1]: micro_output[i] = deepcopy( - micro_output[self._global_ids.index(assoc_active_id)] + micro_output[self._global_ids.index(assoc_active_ids[0])] ) + else: + if self._use_interpolation_adaptivity: + output_1 = micro_output[self._global_ids.index(assoc_active_ids[0])] + output_2 = micro_output[self._global_ids.index(assoc_active_ids[1])] + distance_1 = assoc_dists[0] + distance_2 = assoc_dists[1] + + # Perform weighted interpolation + total_distance = distance_2 + distance_1 + weight_1 = distance_2 / total_distance + weight_2 = distance_1 / total_distance + + # Exclude active state and active steps from interpolation + exclude = list(output_1.keys())[-2:] + + # Interpolate the output + interpolated_output = { + key: weight_1 * output_1[key] + weight_2 * output_2[key] + for key in output_1.keys() + if key not in exclude + } + + micro_output[i] = interpolated_output + else: # If interpolation is not used, copy the output directly + micro_output[i] = deepcopy( + micro_output[self._global_ids.index(assoc_active_ids[0])] + ) assoc_active_ids = list(active_to_inactive_map.keys()) @@ -380,7 +436,35 @@ def _communicate_micro_output( for count, req in enumerate(recv_reqs): output = req.wait() for local_id in active_to_inactive_map[assoc_active_ids[count]]: - micro_output[local_id] = deepcopy(output) + assoc_active_ids = local_sim_is_associated_to[local_id] + assoc_dists = local_sim_association_dists[local_id] + if self._use_interpolation_adaptivity: + if assoc_active_ids[0] == assoc_active_ids[1]: + micro_output[i] = deepcopy( + micro_output[self._global_ids.index(assoc_active_ids[0])] + ) + else: + distance_1 = assoc_dists[0] + distance_2 = assoc_dists[1] + + ## Perform weighted interpolation + total_distance = distance_2 - distance_1 + weight_1 = distance_2 / total_distance + weight_2 = -distance_1 / total_distance + + # Exclude active state and active steps from interpolation + exclude = list(output_1.keys())[-2:] + + # Interpolate the output + interpolated_output = { + key: weight_1 * output_1[key] + weight_2 * output_2[key] + for key in output_1.keys() + if key not in exclude + } + + micro_output[local_id] = interpolated_output + else: # If interpolation is not used, copy the output directly + micro_output[local_id] = deepcopy(output) def _compute_inactive_sims( self, _refine_const, is_sim_active, just_deactivated @@ -395,9 +479,10 @@ def _compute_inactive_sims( if not is_sim_active[i]: # if id is inactive if self._check_for_activation(i, is_sim_active): is_sim_active[i] = True - _sim_is_associated_to_updated[ - i - ] = -2 # Active sim cannot have an associated sim + _sim_is_associated_to_updated[i] = [ + -2, + -2, + ] # Active sim cannot have an associated sim if self._is_sim_on_this_rank[i] and i not in just_deactivated: to_be_activated_ids.append(i) return is_sim_active, _sim_is_associated_to_updated, to_be_activated_ids @@ -421,8 +506,13 @@ def _update_inactive_sims( List of objects of class MicroProblem, which are the micro simulations """ self._is_sim_active = np.copy(_is_sim_active) + to_be_activated_ids_active = [] + for i in range(len(to_be_activated_ids)): + if self._is_sim_active[to_be_activated_ids[i]]: + to_be_activated_ids_active.append(to_be_activated_ids[i]) self._just_deactivated.clear() # Clear the list of sims deactivated in this step + self._just_deactivated_ref.clear() local_sim_is_associated_to = self._sim_is_associated_to[ self._global_ids[0] : self._global_ids[-1] + 1 @@ -432,7 +522,7 @@ def _update_inactive_sims( # global IDs of inactive sims associated to the active sims which are on this rank to_be_activated_map: Dict[int, list] = dict() - for i in to_be_activated_ids: + for i in to_be_activated_ids_active: # Only handle activation of simulations on this rank -- LOCAL SCOPE HERE ON if self._is_sim_on_this_rank[i]: to_be_activated_local_id = self._global_ids.index(i) @@ -442,19 +532,19 @@ def _update_inactive_sims( assoc_active_id = local_sim_is_associated_to[to_be_activated_local_id] if self._is_sim_on_this_rank[ - assoc_active_id + assoc_active_id[0] ]: # Associated active simulation is on the same rank - assoc_active_local_id = self._global_ids.index(assoc_active_id) + assoc_active_local_id = self._global_ids.index(assoc_active_id[0]) micro_sims[to_be_activated_local_id].set_state( micro_sims[assoc_active_local_id].get_state() ) else: # Associated active simulation is not on this rank - if assoc_active_id in to_be_activated_map: - to_be_activated_map[assoc_active_id].append( + if assoc_active_id[0] in to_be_activated_map: + to_be_activated_map[assoc_active_id[0]].append( to_be_activated_local_id ) else: - to_be_activated_map[assoc_active_id] = [ + to_be_activated_map[assoc_active_id[0]] = [ to_be_activated_local_id ] diff --git a/micro_manager/config.py b/micro_manager/config.py index 5a20600a..46df91a7 100644 --- a/micro_manager/config.py +++ b/micro_manager/config.py @@ -54,6 +54,7 @@ def __init__(self, config_file_name): self._dynamic_adaptivity = False self._adaptivity_similarity_measure = "L1" self._adaptivity_output_type = "" + self._use_interpolation_adaptivity = False self._adaptivity_output_n = 1 # Snapshot information @@ -369,6 +370,19 @@ def read_json_micro_manager(self): "adaptivity_settings" ]["every_implicit_iteration"] + use_interpolation = self._data["simulation_params"][ + "adaptivity_settings" + ]["use_interpolation"] + if use_interpolation: + self._use_interpolation_adaptivity = True + self._logger.log_info_rank_zero( + "Micro Manager will use interpolation for adaptivity." + ) + else: + self._logger.log_info_rank_zero( + "Micro Manager will not use interpolation for adaptivity." + ) + if adaptivity_every_implicit_iteration: self._adaptivity_every_implicit_iteration = True self._logger.log_info_rank_zero( @@ -696,6 +710,17 @@ def get_adaptivity_refining_const(self): """ return self._adaptivity_refining_constant + def get_interpolation(self): + """ + Check if user wants to use interpolation for adaptivity. + + Returns + ------- + use_interpolation_adaptivity : bool + True if user wants to use interpolation for adaptivity, False otherwise. + """ + return self._use_interpolation_adaptivity + def get_dynamic_adaptivity(self): """ Get adaptivity for refining constant. diff --git a/micro_manager/micro_manager.py b/micro_manager/micro_manager.py index c4a8881f..e40e17b8 100644 --- a/micro_manager/micro_manager.py +++ b/micro_manager/micro_manager.py @@ -142,6 +142,9 @@ def __init__(self, config_file: str, log_file: str = "") -> None: self._size, ) + self._micro_sims_input_last = None # DECLARATION + self._micro_sims_output_last = None # DECLARATION + # ************** # Public methods # ************** @@ -202,6 +205,9 @@ def solve(self) -> None: micro_sims_output = micro_sim_solve(micro_sims_input, dt) + self._micro_sims_input_last = micro_sims_input + self._micro_sims_output_last = micro_sims_output + # Check if more than a certain percentage of the micro simulations have crashed and terminate if threshold is exceeded if self._interpolate_crashed_sims: crashed_sims_on_all_ranks = np.zeros(self._size, dtype=np.int64) @@ -260,6 +266,10 @@ def solve(self) -> None: mem_usage_n.append(n) self._logger.log_info_rank_zero("Time window {} converged.".format(n)) + self._micro_sims_input_last = None + self._micro_sims_output_last = None + if self._is_adaptivity_on: + self._adaptivity_controller._reset_hist() # Reset first iteration flag for the next time window first_iteration = True @@ -823,11 +833,18 @@ def _solve_micro_simulations_with_adaptivity( List of dicts in which keys are names of data and the values are the data which are required outputs of """ active_sim_ids = self._adaptivity_controller.get_active_sim_ids() + active_sim_ids_to_solve = ( + self._adaptivity_controller.get_active_sim_ids_to_solve() + ) - micro_sims_output = [0] * self._local_number_of_sims + if self._micro_sims_input_last is None: + micro_sims_output = [0] * self._local_number_of_sims + else: + micro_sims_output = self._micro_sims_output_last # Solve all active micro simulations - for active_id in active_sim_ids: + for active_id in active_sim_ids_to_solve: # to use hist + # for active_id in active_sim_ids: # If micro simulation has not crashed in a previous iteration, attempt to solve it if not self._has_sim_crashed[active_id]: # Attempt to solve the micro simulation From f61fd268e4313452bd332ea5bf57ac160fef9b83 Mon Sep 17 00:00:00 2001 From: Jun Chen Date: Mon, 10 Nov 2025 16:43:06 +0100 Subject: [PATCH 10/11] remove interpolation --- micro_manager/adaptivity/adaptivity.py | 37 ++----- micro_manager/adaptivity/global_adaptivity.py | 102 ++++-------------- micro_manager/config.py | 25 ----- 3 files changed, 28 insertions(+), 136 deletions(-) diff --git a/micro_manager/adaptivity/adaptivity.py b/micro_manager/adaptivity/adaptivity.py index e35592ab..f51367d3 100644 --- a/micro_manager/adaptivity/adaptivity.py +++ b/micro_manager/adaptivity/adaptivity.py @@ -34,7 +34,6 @@ def __init__(self, configurator, rank, nsims) -> None: self._adaptivity_type = configurator.get_adaptivity_type() self._adaptivity_output_type = configurator.get_adaptivity_output_type() - self._use_interpolation_adaptivity = configurator.get_interpolation() self._dynamic_adaptivity = configurator.get_dynamic_adaptivity() self._dynamic_refine_const = self._refine_const self._precice_config_file_name = configurator.get_precice_config_file_name() @@ -65,15 +64,11 @@ def __init__(self, configurator, rank, nsims) -> None: self._is_sim_active_ref = self._is_sim_active.copy() self._is_sim_to_solve = self._is_sim_active.copy() self._gobal_data_last = None - self._hist_dists = np.zeros((nsims), dtype=float) # sim_is_associated_to: 1D array with values of associated simulations of inactive simulations. Active simulations have None # Active sims do not have an associated sim # This array is modified in place via the function associate_inactive_to_active - self._sim_is_associated_to = np.full((nsims, 2), -2, dtype=np.intc) - self._sim_association_dists = np.full( - (len(self._is_sim_active), 2), 0, dtype=float - ) + self._sim_is_associated_to = np.full((nsims), -2, dtype=np.intc) self._just_deactivated: list[int] = [] self._just_deactivated_ref: list[int] = [] @@ -394,30 +389,18 @@ def _associate_inactive_to_active(self) -> None: # Add the +1 for the case when the similarity distance matrix is zeros dist_min_start_value = self._max_similarity_dist + 1 - # Associate inactive micro sims to the two most similar active micro sims + # Associate inactive micro sims to active micro sims for inactive_id in inactive_ids: - # Initialize distances and associated IDs - closest_active_ids = [len(self._is_sim_active) - 1, self._is_sim_active - 1] - closest_dists = [dist_min_start_value, dist_min_start_value] + # Begin with a large distance to trigger the search for the most similar active sim + dist_min = dist_min_start_value for active_id in active_ids: - # Get the similarity distance - dist = self._similarity_dists[inactive_id, active_id] - - # Update the closest and second-closest active simulations - if dist < closest_dists[0]: # Found a new closest simulation - closest_dists[1] = closest_dists[0] - closest_active_ids[1] = closest_active_ids[0] - - closest_dists[0] = dist - closest_active_ids[0] = active_id - elif dist < closest_dists[1]: # Found a new second-closest simulation - closest_dists[1] = dist - closest_active_ids[1] = active_id - - # Store the two closest active simulations and their distances - self._sim_is_associated_to[inactive_id] = closest_active_ids - self._sim_association_dists[inactive_id] = closest_dists + # Find most similar active sim for every inactive sim + if self._similarity_dists[inactive_id, active_id] < dist_min: + associated_active_id = active_id + dist_min = self._similarity_dists[inactive_id, active_id] + + self._sim_is_associated_to[inactive_id] = associated_active_id def _check_for_activation( self, inactive_id: int, is_sim_active: np.ndarray diff --git a/micro_manager/adaptivity/global_adaptivity.py b/micro_manager/adaptivity/global_adaptivity.py index 86564ecd..cd85854a 100644 --- a/micro_manager/adaptivity/global_adaptivity.py +++ b/micro_manager/adaptivity/global_adaptivity.py @@ -317,7 +317,7 @@ def log_metrics(self, n: int) -> None: for global_id in self._global_ids: if not self._is_sim_active[global_id]: assoc_rank = int( - ranks_of_sims[self._sim_is_associated_to[global_id][0]] + ranks_of_sims[self._sim_is_associated_to[global_id]] ) if not assoc_rank in assoc_ranks: assoc_ranks.append(assoc_rank) @@ -374,9 +374,6 @@ def _communicate_micro_output( local_sim_is_associated_to = self._sim_is_associated_to[ self._global_ids[0] : self._global_ids[-1] + 1 ] - local_sim_association_dists = self._sim_association_dists[ - self._global_ids[0] : self._global_ids[-1] + 1 - ] # Keys are global IDs of active simulations associated to inactive # simulations on this rank. Values are global IDs of the inactive @@ -384,49 +381,17 @@ def _communicate_micro_output( active_to_inactive_map: Dict[int, list] = dict() for i in inactive_local_ids: - assoc_active_ids = local_sim_is_associated_to[i] - assoc_dists = local_sim_association_dists[i] - - # # Gather global IDs of associated active simulations not on this rank - # if not all(self._is_sim_on_this_rank[assoc_active_ids]): - # for assoc_active_id in assoc_active_ids: - # if not self._is_sim_on_this_rank[assoc_active_id]: - # if assoc_active_id in active_to_inactive_map: - # active_to_inactive_map[assoc_active_id].append(i) - # else: - # active_to_inactive_map[assoc_active_id] = [i] - # else: # If associated active simulations are on this rank, interpolate the output - if assoc_active_ids[0] == assoc_active_ids[1]: + assoc_active_id = local_sim_is_associated_to[i] + # Gather global IDs of associated active simulations not on this rank + if not self._is_sim_on_this_rank[assoc_active_id]: + if assoc_active_id in active_to_inactive_map: + active_to_inactive_map[assoc_active_id].append(i) + else: + active_to_inactive_map[assoc_active_id] = [i] + else: # If associated active simulation is on this rank, copy the output directly micro_output[i] = deepcopy( - micro_output[self._global_ids.index(assoc_active_ids[0])] + micro_output[self._global_ids.index(assoc_active_id)] ) - else: - if self._use_interpolation_adaptivity: - output_1 = micro_output[self._global_ids.index(assoc_active_ids[0])] - output_2 = micro_output[self._global_ids.index(assoc_active_ids[1])] - distance_1 = assoc_dists[0] - distance_2 = assoc_dists[1] - - # Perform weighted interpolation - total_distance = distance_2 + distance_1 - weight_1 = distance_2 / total_distance - weight_2 = distance_1 / total_distance - - # Exclude active state and active steps from interpolation - exclude = list(output_1.keys())[-2:] - - # Interpolate the output - interpolated_output = { - key: weight_1 * output_1[key] + weight_2 * output_2[key] - for key in output_1.keys() - if key not in exclude - } - - micro_output[i] = interpolated_output - else: # If interpolation is not used, copy the output directly - micro_output[i] = deepcopy( - micro_output[self._global_ids.index(assoc_active_ids[0])] - ) assoc_active_ids = list(active_to_inactive_map.keys()) @@ -436,35 +401,7 @@ def _communicate_micro_output( for count, req in enumerate(recv_reqs): output = req.wait() for local_id in active_to_inactive_map[assoc_active_ids[count]]: - assoc_active_ids = local_sim_is_associated_to[local_id] - assoc_dists = local_sim_association_dists[local_id] - if self._use_interpolation_adaptivity: - if assoc_active_ids[0] == assoc_active_ids[1]: - micro_output[i] = deepcopy( - micro_output[self._global_ids.index(assoc_active_ids[0])] - ) - else: - distance_1 = assoc_dists[0] - distance_2 = assoc_dists[1] - - ## Perform weighted interpolation - total_distance = distance_2 - distance_1 - weight_1 = distance_2 / total_distance - weight_2 = -distance_1 / total_distance - - # Exclude active state and active steps from interpolation - exclude = list(output_1.keys())[-2:] - - # Interpolate the output - interpolated_output = { - key: weight_1 * output_1[key] + weight_2 * output_2[key] - for key in output_1.keys() - if key not in exclude - } - - micro_output[local_id] = interpolated_output - else: # If interpolation is not used, copy the output directly - micro_output[local_id] = deepcopy(output) + micro_output[local_id] = deepcopy(output) def _compute_inactive_sims( self, _refine_const, is_sim_active, just_deactivated @@ -479,16 +416,13 @@ def _compute_inactive_sims( if not is_sim_active[i]: # if id is inactive if self._check_for_activation(i, is_sim_active): is_sim_active[i] = True - _sim_is_associated_to_updated[i] = [ - -2, - -2, - ] # Active sim cannot have an associated sim + _sim_is_associated_to_updated[i] = -2 # Active sim cannot have an associated sim if self._is_sim_on_this_rank[i] and i not in just_deactivated: to_be_activated_ids.append(i) return is_sim_active, _sim_is_associated_to_updated, to_be_activated_ids def _update_inactive_sims( - self, + self, micro_sims: list, _is_sim_active, _sim_is_associated_to_updated, @@ -532,19 +466,19 @@ def _update_inactive_sims( assoc_active_id = local_sim_is_associated_to[to_be_activated_local_id] if self._is_sim_on_this_rank[ - assoc_active_id[0] + assoc_active_id ]: # Associated active simulation is on the same rank - assoc_active_local_id = self._global_ids.index(assoc_active_id[0]) + assoc_active_local_id = self._global_ids.index(assoc_active_id) micro_sims[to_be_activated_local_id].set_state( micro_sims[assoc_active_local_id].get_state() ) else: # Associated active simulation is not on this rank - if assoc_active_id[0] in to_be_activated_map: - to_be_activated_map[assoc_active_id[0]].append( + if assoc_active_id in to_be_activated_map: + to_be_activated_map[assoc_active_id].append( to_be_activated_local_id ) else: - to_be_activated_map[assoc_active_id[0]] = [ + to_be_activated_map[assoc_active_id] = [ to_be_activated_local_id ] diff --git a/micro_manager/config.py b/micro_manager/config.py index 46df91a7..5a20600a 100644 --- a/micro_manager/config.py +++ b/micro_manager/config.py @@ -54,7 +54,6 @@ def __init__(self, config_file_name): self._dynamic_adaptivity = False self._adaptivity_similarity_measure = "L1" self._adaptivity_output_type = "" - self._use_interpolation_adaptivity = False self._adaptivity_output_n = 1 # Snapshot information @@ -370,19 +369,6 @@ def read_json_micro_manager(self): "adaptivity_settings" ]["every_implicit_iteration"] - use_interpolation = self._data["simulation_params"][ - "adaptivity_settings" - ]["use_interpolation"] - if use_interpolation: - self._use_interpolation_adaptivity = True - self._logger.log_info_rank_zero( - "Micro Manager will use interpolation for adaptivity." - ) - else: - self._logger.log_info_rank_zero( - "Micro Manager will not use interpolation for adaptivity." - ) - if adaptivity_every_implicit_iteration: self._adaptivity_every_implicit_iteration = True self._logger.log_info_rank_zero( @@ -710,17 +696,6 @@ def get_adaptivity_refining_const(self): """ return self._adaptivity_refining_constant - def get_interpolation(self): - """ - Check if user wants to use interpolation for adaptivity. - - Returns - ------- - use_interpolation_adaptivity : bool - True if user wants to use interpolation for adaptivity, False otherwise. - """ - return self._use_interpolation_adaptivity - def get_dynamic_adaptivity(self): """ Get adaptivity for refining constant. From 9f1d7cb229e500e1bb800aa1a592f3735d731901 Mon Sep 17 00:00:00 2001 From: Jun Chen Date: Fri, 28 Nov 2025 15:48:12 +0100 Subject: [PATCH 11/11] update history dist computation --- micro_manager/adaptivity/adaptivity.py | 77 ++++++++++--------- micro_manager/adaptivity/global_adaptivity.py | 12 ++- micro_manager/micro_manager.py | 4 +- 3 files changed, 47 insertions(+), 46 deletions(-) diff --git a/micro_manager/adaptivity/adaptivity.py b/micro_manager/adaptivity/adaptivity.py index f51367d3..0cb6b965 100644 --- a/micro_manager/adaptivity/adaptivity.py +++ b/micro_manager/adaptivity/adaptivity.py @@ -1,6 +1,7 @@ """ Functionality for adaptive initialization and control of micro simulations """ +from copy import deepcopy import sys import os import numpy as np @@ -63,7 +64,7 @@ def __init__(self, configurator, rank, nsims) -> None: self._is_sim_active = np.array([True] * nsims, dtype=np.bool_) self._is_sim_active_ref = self._is_sim_active.copy() self._is_sim_to_solve = self._is_sim_active.copy() - self._gobal_data_last = None + self._global_data_last = None # sim_is_associated_to: 1D array with values of associated simulations of inactive simulations. Active simulations have None # Active sims do not have an associated sim @@ -188,32 +189,8 @@ def _update_similarity_dists(self, dt: float, data: dict) -> None: self._similarity_dists += dt * self._similarity_measure(data_vals) - def _update_history_dists(self, data_last: dict, data: dict) -> None: - """ - Calculate metric which determines if one micro simulations is similar enough to the history. - - Parameters - ---------- - data_last : dict - Data from last time step to be used in similarity distance calculation - data : dict - Data to be used in similarity distance calculation - """ - if data_last is None: - self._hist_dists = np.zeros((len(self._is_sim_active)), dtype=float) - return - - for name in data.keys(): - data_vals = np.array(data[name]) - data_last_vals = np.array(data_last[name]) - self._hist_dists += np.abs(data_vals - data_last_vals) - def _reset_hist(self) -> None: - """ - Reset history distances to zero. - """ - self._hist_dists = np.zeros((len(self._is_sim_active)), dtype=float) - self._gobal_data_last = None + self._global_data_last = None def _get_addition(self) -> float: """ @@ -285,7 +262,7 @@ def _get_addition(self) -> float: addition = min( self._min_addition, min( - (1 + 1.0 / (min(0.0, min_convergence) - 1.0)) ** alpha, + (1 + 1.0 / (min_convergence - 1.0)) ** alpha, float(last_line[2]) / self._max_similarity_dist / self._coarse_const, @@ -365,18 +342,44 @@ def _compute_active_sims(self, use_dyn) -> tuple: just_deactivated.append(i) return is_sim_active, just_deactivated - def _compute_sims_to_solve(self) -> None: + def _compute_sims_to_solve(self, global_data: dict) -> None: """ - Compute which simulations to solve. Simulations to solve are active simulations and inactive simulations which were just deactivated. + Compute which simulations to solve. """ - Cr = self._dynamic_refine_const - tol_u = max(self._hist_dists[self._is_sim_to_solve]) * Cr - if sum(self._is_sim_active) > 1: - for i in range(self._is_sim_active.size): - if self._is_sim_active[i] and self._hist_dists[i] >= tol_u: - self._hist_dists[i] = 0.0 - else: - self._is_sim_to_solve[i] = False + self._is_sim_to_solve = self._is_sim_active.copy() + hist_dist = 0.0 + + if self._global_data_last is None: + self._global_data_last = deepcopy(global_data) + return + else: + tol_u = ( + self._dynamic_refine_const + * self._max_similarity_dist + / sum(self._is_sim_active) + ) + self._logger.log_info("tol_u: {}".format(tol_u)) + if sum(self._is_sim_active) > 1: + for i in range(self._is_sim_active.size): + if self._is_sim_active[i]: + for name in global_data.keys(): + hist_dist = np.abs( + self._global_data_last[name][i] - global_data[name][i] + ) + self._logger.log_info( + "_global_data_last: {}, global_data: {}, hist_dist: {} for cell {}".format( + self._global_data_last[name][i], + global_data[name][i], + hist_dist, + i, + ) + ) + if hist_dist >= tol_u: + self._global_data_last[name][i] = global_data[name][i] + else: + self._is_sim_to_solve[i] = False + if sum(self._is_sim_to_solve) == 0: + self._is_sim_to_solve = self._is_sim_active.copy() def _associate_inactive_to_active(self) -> None: """ diff --git a/micro_manager/adaptivity/global_adaptivity.py b/micro_manager/adaptivity/global_adaptivity.py index cd85854a..bdc859d0 100644 --- a/micro_manager/adaptivity/global_adaptivity.py +++ b/micro_manager/adaptivity/global_adaptivity.py @@ -150,10 +150,6 @@ def compute_adaptivity( self._MPI_local_rank == 0 ): # Only the first rank in the node updates the similarity distances self._update_similarity_dists(dt, global_data_for_adaptivity) - self._update_history_dists( - self._gobal_data_last, global_data_for_adaptivity - ) - self._gobal_data_last = deepcopy(global_data_for_adaptivity) self._comm_node.Barrier() # Wait for the similarity distances to be updated on all ranks of the node @@ -195,7 +191,7 @@ def compute_adaptivity( _sim_is_associated_to_dyn, _to_be_activated_ids_sta, ) - self._compute_sims_to_solve() + self._compute_sims_to_solve(global_data_for_adaptivity) self._logger.log_info("sims to solve: {}".format(np.sum(self._is_sim_to_solve))) self._associate_inactive_to_active() @@ -416,13 +412,15 @@ def _compute_inactive_sims( if not is_sim_active[i]: # if id is inactive if self._check_for_activation(i, is_sim_active): is_sim_active[i] = True - _sim_is_associated_to_updated[i] = -2 # Active sim cannot have an associated sim + _sim_is_associated_to_updated[ + i + ] = -2 # Active sim cannot have an associated sim if self._is_sim_on_this_rank[i] and i not in just_deactivated: to_be_activated_ids.append(i) return is_sim_active, _sim_is_associated_to_updated, to_be_activated_ids def _update_inactive_sims( - self, + self, micro_sims: list, _is_sim_active, _sim_is_associated_to_updated, diff --git a/micro_manager/micro_manager.py b/micro_manager/micro_manager.py index e40e17b8..ad39e4e5 100644 --- a/micro_manager/micro_manager.py +++ b/micro_manager/micro_manager.py @@ -843,8 +843,8 @@ def _solve_micro_simulations_with_adaptivity( micro_sims_output = self._micro_sims_output_last # Solve all active micro simulations - for active_id in active_sim_ids_to_solve: # to use hist - # for active_id in active_sim_ids: + # for active_id in active_sim_ids_to_solve: # to use hist + for active_id in active_sim_ids: # If micro simulation has not crashed in a previous iteration, attempt to solve it if not self._has_sim_crashed[active_id]: # Attempt to solve the micro simulation