diff --git a/CHANGELOG.md b/CHANGELOG.md index c07761f..af8bb05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## latest +- Remove the `adaptivity_data` data structure and handle all adaptivity data internally https://github.com/precice/micro-manager/pull/137 - Improve logging by wrapping Python logger in a class https://github.com/precice/micro-manager/pull/133 - Refactor large parts of solve and adaptivity to group datasets and simplify handling https://github.com/precice/micro-manager/pull/135 - Add information about adaptivity tuning parameters https://github.com/precice/micro-manager/pull/131 diff --git a/micro_manager/adaptivity/adaptivity.py b/micro_manager/adaptivity/adaptivity.py index bf0fa3c..4feb63b 100644 --- a/micro_manager/adaptivity/adaptivity.py +++ b/micro_manager/adaptivity/adaptivity.py @@ -5,12 +5,13 @@ from math import exp from typing import Callable from warnings import warn +from micro_manager.tools.logging_wrapper import Logger import numpy as np class AdaptivityCalculator: - def __init__(self, configurator) -> None: + def __init__(self, configurator, rank) -> None: """ Class constructor. @@ -18,7 +19,8 @@ def __init__(self, configurator) -> None: ---------- configurator : object of class Config Object which has getter functions to get parameters defined in the configuration file. - logger : Logger defined from the standard package logging + rank : int + Rank of the MPI communicator. """ self._refine_const = configurator.get_adaptivity_refining_const() self._coarse_const = configurator.get_adaptivity_coarsening_const() @@ -29,10 +31,20 @@ def __init__(self, configurator) -> None: self._coarse_tol = 0.0 self._ref_tol = 0.0 + self._rank = rank + self._similarity_measure = self._get_similarity_measure( configurator.get_adaptivity_similarity_measure() ) + self._metrics_logger = Logger( + "Adaptivity", "adaptivity-metrics.csv", rank, csv_logger=True + ) + + self._metrics_logger.log_info_one_rank( + "Time Window,Avg Active Sims,Avg Inactive Sims,Max Active,Max Inactive" + ) + def _get_similarity_dists( self, dt: float, similarity_dists: np.ndarray, data: dict ) -> np.ndarray: diff --git a/micro_manager/adaptivity/global_adaptivity.py b/micro_manager/adaptivity/global_adaptivity.py index bf7b700..d128b58 100644 --- a/micro_manager/adaptivity/global_adaptivity.py +++ b/micro_manager/adaptivity/global_adaptivity.py @@ -42,11 +42,24 @@ def __init__( comm : MPI.COMM_WORLD Global communicator of MPI. """ - super().__init__(configurator) + super().__init__(configurator, rank) self._global_ids = global_ids self._comm = comm self._rank = rank + # similarity_dists: 2D array having similarity distances between each micro simulation pair + self._similarity_dists = np.zeros( + (global_number_of_sims, global_number_of_sims) + ) + + # is_sim_active: 1D array having state (active or inactive) of each micro simulation + # Start adaptivity calculation with all sims active + self._is_sim_active = np.array([True] * global_number_of_sims) + + # 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 + self._sim_is_associated_to = np.full((global_number_of_sims), -2, dtype=np.intc) + local_number_of_sims = len(global_ids) # Create a map of micro simulation global IDs and the ranks on which they are @@ -65,13 +78,17 @@ def __init__( if self._rank_of_sim[i] == self._rank: self._is_sim_on_this_rank[i] = True + # Copies of variables for checkpointing + self._similarity_dists_cp = None + self._is_sim_active_cp = None + self._sim_is_associated_to_cp = None + def compute_adaptivity( self, dt: float, micro_sims: list, - adaptivity_data_nm1: list, data_for_adaptivity: dict, - ) -> tuple: + ) -> None: """ Compute adaptivity globally based on similarity distances and micro simulation states @@ -81,21 +98,8 @@ def compute_adaptivity( Current time step of the macro-micro coupled problem micro_sims : list List of objects of class MicroProblem, which are the micro simulations - adaptivity_data_nm1 : list - List of numpy arrays: - similarity_dists (2D array having similarity distances between each micro simulation pair) - is_sim_active (1D array having state (active or inactive) of each micro simulation) - sim_is_associated_to (1D array with values of associated simulations of inactive simulations. Active simulations have None) data_for_adaptivity : dict Dictionary with keys as names of data to be used in the similarity calculation, and values as the respective data for the micro simulations - - Results - ------- - list - List of numpy arrays: - similarity_dists (2D array having similarity distances between each micro simulation pair) - is_sim_active (1D array having state (active or inactive) of each micro simulation) - sim_is_associated_to (1D array with values of associated simulations of inactive simulations. Active simulations have None) """ for name in data_for_adaptivity.keys(): if name not in self._adaptivity_data_names: @@ -112,71 +116,56 @@ def compute_adaptivity( global_data_for_adaptivity[name] = np.concatenate((data_as_list[:]), axis=0) similarity_dists = self._get_similarity_dists( - dt, adaptivity_data_nm1[0], global_data_for_adaptivity + dt, self._similarity_dists, global_data_for_adaptivity ) - is_sim_active = self._update_active_sims( - similarity_dists, adaptivity_data_nm1[1] - ) + is_sim_active = self._update_active_sims(similarity_dists, self._is_sim_active) is_sim_active, sim_is_associated_to = self._update_inactive_sims( - similarity_dists, is_sim_active, adaptivity_data_nm1[2], micro_sims + similarity_dists, is_sim_active, self._sim_is_associated_to, micro_sims ) + sim_is_associated_to = self._associate_inactive_to_active( similarity_dists, is_sim_active, sim_is_associated_to ) - return similarity_dists, is_sim_active, sim_is_associated_to + # Update member variables + self._similarity_dists = similarity_dists + self._is_sim_active = is_sim_active + self._sim_is_associated_to = sim_is_associated_to - def get_active_sim_ids(self, is_sim_active: np.array) -> np.ndarray: + def get_active_sim_ids(self) -> np.ndarray: """ Get the ids of active simulations. - Parameters - ---------- - is_sim_active : numpy array - 1D array having state (active or inactive) of each micro simulation - Returns ------- numpy array 1D array of active simulation ids """ - return np.where(is_sim_active[self._global_ids[0] : self._global_ids[-1] + 1])[ - 0 - ] + return np.where( + self._is_sim_active[self._global_ids[0] : self._global_ids[-1] + 1] + )[0] - def get_inactive_sim_ids(self, is_sim_active: np.array) -> np.ndarray: + def get_inactive_sim_ids(self) -> np.ndarray: """ Get the ids of inactive simulations. - Parameters - ---------- - is_sim_active : numpy array - 1D array having state (active or inactive) of each micro simulation - Returns ------- numpy array 1D array of inactive simulation ids """ return np.where( - is_sim_active[self._global_ids[0] : self._global_ids[-1] + 1] == False + self._is_sim_active[self._global_ids[0] : self._global_ids[-1] + 1] == False )[0] - def get_full_field_micro_output( - self, adaptivity_data: list, micro_output: list - ) -> list: + def get_full_field_micro_output(self, micro_output: list) -> list: """ Get the full field micro output from active simulations to inactive simulations. Parameters ---------- - adaptivity_data : list - List of numpy arrays: - similarity_dists (2D array having similarity distances between each micro simulation pair) - is_sim_active (1D array having state (active or inactive) of each micro simulation) - sim_is_associated_to (1D array with values of associated simulations of inactive simulations. Active simulations have None) micro_output : list List of dicts having individual output of each simulation. Only the active simulation outputs are entered. @@ -186,17 +175,25 @@ def get_full_field_micro_output( List of dicts having individual output of each simulation. Active and inactive simulation outputs are entered. """ micro_sims_output = deepcopy(micro_output) - self._communicate_micro_output(adaptivity_data[1:3], micro_sims_output) + self._communicate_micro_output( + self._is_sim_active, self._sim_is_associated_to, micro_sims_output + ) return micro_sims_output - def log_metrics(self, logger, adaptivity_data: list, n: int) -> None: - """ """ - is_sim_active = adaptivity_data[1] - global_active_sims = np.count_nonzero(is_sim_active) - global_inactive_sims = np.count_nonzero(is_sim_active == False) + def log_metrics(self, n: int) -> None: + """ + Log metrics for global adaptivity. - logger.log_info_one_rank( + Parameters + ---------- + n : int + Time step count at which the metrics are logged + """ + global_active_sims = np.count_nonzero(self._is_sim_active) + global_inactive_sims = np.count_nonzero(self._is_sim_active == False) + + self._metrics_logger.log_info_one_rank( "{},{},{},{},{}".format( n, np.mean(global_active_sims), @@ -206,9 +203,26 @@ def log_metrics(self, logger, adaptivity_data: list, n: int) -> None: ) ) + def write_checkpoint(self) -> None: + """ + Write checkpoint. + """ + self._similarity_dists_cp = np.copy(self._similarity_dists) + self._is_sim_active_cp = np.copy(self._is_sim_active) + self._sim_is_associated_to_cp = np.copy(self._sim_is_associated_to) + + def read_checkpoint(self) -> None: + """ + Read checkpoint. + """ + self._similarity_dists = np.copy(self._similarity_dists_cp) + self._is_sim_active = np.copy(self._is_sim_active_cp) + self._sim_is_associated_to = np.copy(self._sim_is_associated_to_cp) + def _communicate_micro_output( self, - adaptivity_data: list, + is_sim_active: np.ndarray, + sim_is_associated_to: np.ndarray, micro_output: list, ) -> None: """ @@ -217,17 +231,13 @@ def _communicate_micro_output( Parameters ---------- - micro_sims : list - List of objects of class MicroProblem, which are the micro simulations - adaptivity_data : list - List of numpy arrays: - is_sim_active (1D array having state (active or inactive) of each micro simulation) - sim_is_associated_to (1D array with values of associated simulations of inactive simulations. Active simulations have None) + is_sim_active : np.ndarray + 1D array having state (active or inactive) of each micro simulation + sim_is_associated_to : np.ndarray + 1D array with values of associated simulations of inactive simulations. Active simulations have None micro_output : list List of dicts having individual output of each simulation. Only the active simulation outputs are entered. """ - is_sim_active = adaptivity_data[0] - sim_is_associated_to = adaptivity_data[1] inactive_local_ids = np.where( is_sim_active[self._global_ids[0] : self._global_ids[-1] + 1] == False diff --git a/micro_manager/adaptivity/local_adaptivity.py b/micro_manager/adaptivity/local_adaptivity.py index c89f229..8348030 100644 --- a/micro_manager/adaptivity/local_adaptivity.py +++ b/micro_manager/adaptivity/local_adaptivity.py @@ -10,7 +10,7 @@ class LocalAdaptivityCalculator(AdaptivityCalculator): - def __init__(self, configurator, comm) -> None: + def __init__(self, configurator, rank, comm, num_sims) -> None: """ Class constructor. @@ -20,17 +20,34 @@ def __init__(self, configurator, comm) -> None: Object which has getter functions to get parameters defined in the configuration file. comm : MPI.COMM_WORLD Global communicator of MPI. + num_sims : int + Number of micro simulations. """ - super().__init__(configurator) + super().__init__(configurator, rank) self._comm = comm + # similarity_dists: 2D array having similarity distances between each micro simulation pair + self._similarity_dists = np.zeros((num_sims, num_sims)) + + # is_sim_active: 1D array having state (active or inactive) of each micro simulation + # Start adaptivity calculation with all sims active + self._is_sim_active = np.array([True] * num_sims) + + # 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 + self._sim_is_associated_to = np.full((num_sims), -2, dtype=np.intc) + + # Copies of variables for checkpointing + self._similarity_dists_cp = None + self._is_sim_active_cp = None + self._sim_is_associated_to_cp = None + def compute_adaptivity( self, dt, micro_sims, - adaptivity_data_nm1: list, data_for_adaptivity: dict, - ) -> tuple: + ) -> None: """ Compute adaptivity locally (within a rank). @@ -40,16 +57,10 @@ def compute_adaptivity( Current time step micro_sims : list List containing simulation objects - adaptivity_data_nm1 : list - List of numpy arrays: similarity_dists (2D array having similarity distances between each micro simulation pair), is_sim_active (1D array having state (active or inactive) of each micro simulation), sim_is_associated_to (1D array with values of associated simulations of inactive simulations. Active simulations have None) data_for_adaptivity : dict A dictionary containing the names of the data to be used in adaptivity as keys and information on whether the data are scalar or vector as values. - Returns - ------- - list - List of numpy arrays: similarity_dists (2D array having similarity distances between each micro simulation pair), is_sim_active (1D array having state (active or inactive) of each micro simulation), sim_is_associated_to (1D array with values of associated simulations of inactive simulations. Active simulations have None) """ for name in data_for_adaptivity.keys(): if name not in self._adaptivity_data_names: @@ -60,69 +71,52 @@ def compute_adaptivity( ) similarity_dists = self._get_similarity_dists( - dt, adaptivity_data_nm1[0], data_for_adaptivity + dt, self._similarity_dists, data_for_adaptivity ) - # Operation done globally if global adaptivity is chosen - is_sim_active = self._update_active_sims( - similarity_dists, adaptivity_data_nm1[1] - ) + is_sim_active = self._update_active_sims(similarity_dists, self._is_sim_active) is_sim_active, sim_is_associated_to = self._update_inactive_sims( - similarity_dists, is_sim_active, adaptivity_data_nm1[2], micro_sims + similarity_dists, is_sim_active, self._sim_is_associated_to, micro_sims ) sim_is_associated_to = self._associate_inactive_to_active( similarity_dists, is_sim_active, sim_is_associated_to ) - return similarity_dists, is_sim_active, sim_is_associated_to + # Update member variables + self._similarity_dists = similarity_dists + self._is_sim_active = is_sim_active + self._sim_is_associated_to = sim_is_associated_to - def get_active_sim_ids(self, is_sim_active) -> np.ndarray: + def get_active_sim_ids(self) -> np.ndarray: """ Get the ids of active simulations. - Parameters - ---------- - is_sim_active : numpy array - 1D array having state (active or inactive) of each micro simulation - Returns ------- numpy array 1D array of active simulation ids """ - return np.where(is_sim_active)[0] + return np.where(self._is_sim_active)[0] - def get_inactive_sim_ids(self, is_sim_active: np.array) -> np.ndarray: + def get_inactive_sim_ids(self) -> np.ndarray: """ Get the ids of inactive simulations. - Parameters - ---------- - is_sim_active : numpy array - 1D array having state (active or inactive) of each micro simulation - Returns ------- numpy array 1D array of inactive simulation ids """ - return np.where(is_sim_active == False)[0] + return np.where(self._is_sim_active == False)[0] - def get_full_field_micro_output( - self, adaptivity_data: list, micro_output: list - ) -> list: + def get_full_field_micro_output(self, micro_output: list) -> list: """ Get the full field micro output from active simulations to inactive simulations. Parameters ---------- - adaptivity_data : list - List of numpy arrays: - similarity_dists (2D array having similarity distances between each micro simulation pair) - is_sim_active (1D array having state (active or inactive) of each micro simulation) - sim_is_associated_to (1D array with values of associated simulations of inactive simulations. Active simulations have None) micro_output : list List of dicts having individual output of each simulation. Only the active simulation outputs are entered. @@ -133,29 +127,30 @@ def get_full_field_micro_output( """ micro_sims_output = deepcopy(micro_output) - sim_is_associated_to = adaptivity_data[2] - - inactive_sim_ids = self.get_inactive_sim_ids(adaptivity_data[1]) + inactive_sim_ids = self.get_inactive_sim_ids() for inactive_id in inactive_sim_ids: micro_sims_output[inactive_id] = deepcopy( - micro_sims_output[sim_is_associated_to[inactive_id]] + micro_sims_output[self._sim_is_associated_to[inactive_id]] ) return micro_sims_output - def log_metrics(self, logger, adaptivity_list: list, n: int) -> None: - """ """ - is_sim_active = adaptivity_list[1] + def log_metrics(self, n: int) -> None: + """ + Log metrics for local adaptivity. + Parameters + ---------- + """ # MPI Gather is necessary as local adaptivity only stores local data - local_active_sims = np.count_nonzero(is_sim_active) + local_active_sims = np.count_nonzero(self._is_sim_active) global_active_sims = self._comm.gather(local_active_sims) - local_inactive_sims = np.count_nonzero(is_sim_active == False) + local_inactive_sims = np.count_nonzero(self._is_sim_active == False) global_inactive_sims = self._comm.gather(local_inactive_sims) - logger.log_info_one_rank( + self._metrics_logger.log_info_one_rank( "{},{},{},{},{}".format( n, np.mean(global_active_sims), @@ -165,6 +160,22 @@ def log_metrics(self, logger, adaptivity_list: list, n: int) -> None: ) ) + def write_checkpoint(self) -> None: + """ + Write checkpoint. + """ + self._similarity_dists_cp = np.copy(self._similarity_dists) + self._is_sim_active_cp = np.copy(self._is_sim_active) + self._sim_is_associated_to_cp = np.copy(self._sim_is_associated_to) + + def read_checkpoint(self) -> None: + """ + Read checkpoint. + """ + self._similarity_dists = np.copy(self._similarity_dists_cp) + self._is_sim_active = np.copy(self._is_sim_active_cp) + self._sim_is_associated_to = np.copy(self._sim_is_associated_to_cp) + def _update_inactive_sims( self, similarity_dists: np.ndarray, diff --git a/micro_manager/micro_manager.py b/micro_manager/micro_manager.py index 56b7023..cdb2d3a 100644 --- a/micro_manager/micro_manager.py +++ b/micro_manager/micro_manager.py @@ -17,7 +17,6 @@ import sys import time import inspect -from copy import deepcopy from typing import Dict from warnings import warn from typing import Callable @@ -106,18 +105,7 @@ def __init__(self, config_file: str) -> None: self._is_adaptivity_on = self._config.turn_on_adaptivity() if self._is_adaptivity_on: - self._adaptivity_logger = Logger( - "Adaptivity", "adaptivity-metrics.csv", self._rank, csv_logger=True - ) - - self._adaptivity_logger.log_info_one_rank( - "Time Window,Avg Active Sims,Avg Inactive Sims,Max Active,Max Inactive" - ) - - self._number_of_sims_for_adaptivity: int = 0 - self._data_for_adaptivity: Dict[str, np.ndarray] = dict() - self._adaptivity_type = self._config.get_adaptivity_type() self._adaptivity_data_names = self._config.get_data_for_adaptivity() @@ -161,49 +149,22 @@ def solve(self) -> None: """ t, n = 0, 0 t_checkpoint, n_checkpoint = 0, 0 - adaptivity_data_cp: list = [] sim_states_cp = [None] * self._local_number_of_sims micro_sim_solve = self._get_solve_variant() dt = min(self._participant.get_max_time_step_size(), self._micro_dt) - # adaptivity_data is a list of numpy arrays: - # 1. similarity_dists (2D array having similarity distances between each micro simulation pair) - # 2. is_sim_active (1D array having state (active or inactive) of each micro simulation) - # 3. sim_is_associated_to (1D array with values of associated simulations of inactive simulations. Active simulations have None) - adaptivity_data: list = [] - if self._is_adaptivity_on: - adaptivity_data.append( - np.zeros( - ( - self._number_of_sims_for_adaptivity, - self._number_of_sims_for_adaptivity, - ) - ) - ) - - # Start adaptivity calculation with all sims active - adaptivity_data.append( - np.array([True] * self._number_of_sims_for_adaptivity) - ) - - # Active sims do not have an associated sim - adaptivity_data.append( - np.full((self._number_of_sims_for_adaptivity), -2, dtype=np.intc) - ) - # If micro simulations have been initialized, compute adaptivity before starting the coupling if self._micro_sims_init: self._logger.log_info_one_rank( "Micro simulations have been initialized, so adaptivity will be computed before the coupling begins." ) - adaptivity_data = self._adaptivity_controller.compute_adaptivity( + self._adaptivity_controller.compute_adaptivity( dt, self._micro_sims, - adaptivity_data, self._data_for_adaptivity, ) @@ -223,21 +184,19 @@ def solve(self) -> None: if self._is_adaptivity_on: if not self._adaptivity_in_every_implicit_step: start_time = time.process_time() - adaptivity_data = ( - self._adaptivity_controller.compute_adaptivity( - dt, - self._micro_sims, - adaptivity_data, - self._data_for_adaptivity, - ) + self._adaptivity_controller.compute_adaptivity( + dt, + self._micro_sims, + self._data_for_adaptivity, ) + end_time = time.process_time() adaptivity_cpu_time = end_time - start_time # Only checkpoint the adaptivity configuration if adaptivity is computed # once in every time window - adaptivity_data_cp = deepcopy(adaptivity_data) + self._adaptivity_controller.write_checkpoint() active_sim_ids = ( self._adaptivity_controller.get_active_sim_ids() @@ -248,7 +207,7 @@ def solve(self) -> None: micro_sims_input = self._read_data_from_precice(dt) - micro_sims_output = micro_sim_solve(micro_sims_input, dt, adaptivity_data) + micro_sims_output = micro_sim_solve(micro_sims_input, dt) if self._output_adaptivity_cpu_time: for i in range(self._local_number_of_sims): @@ -279,12 +238,10 @@ def solve(self) -> None: self._write_data_to_precice(micro_sims_output) - t += dt # increase internal time when time step is done. - n += 1 # increase counter + t += dt + n += 1 - self._participant.advance( - dt - ) # notify preCICE that time step of size dt is complete + self._participant.advance(dt) # Revert micro simulations to their last checkpoints if required if self._participant.requires_reading_checkpoint(): @@ -296,7 +253,7 @@ def solve(self) -> None: # If adaptivity is computed only once per time window, the states of sims need to be reset too if self._is_adaptivity_on: if not self._adaptivity_in_every_implicit_step: - adaptivity_data = deepcopy(adaptivity_data_cp) + self._adaptivity_controller.read_checkpoint() if ( self._participant.is_time_window_complete() @@ -311,9 +268,7 @@ def solve(self) -> None: and n % self._adaptivity_output_n == 0 and self._rank == 0 ): - self._adaptivity_controller.log_metrics( - self._adaptivity_logger, adaptivity_data, n - ) + self._adaptivity_controller.log_metrics(n) self._logger.log_info_one_rank("Time window {} converged.".format(n)) @@ -451,12 +406,13 @@ def initialize(self) -> None: ) if self._is_adaptivity_on: - if self._adaptivity_type == "local": + if self._config.get_adaptivity_type() == "local": self._adaptivity_controller: LocalAdaptivityCalculator = ( - LocalAdaptivityCalculator(self._config, self._comm) + LocalAdaptivityCalculator( + self._config, self._rank, self._comm, self._local_number_of_sims + ) ) - self._number_of_sims_for_adaptivity = self._local_number_of_sims - elif self._adaptivity_type == "global": + elif self._config.get_adaptivity_type() == "global": self._adaptivity_controller: GlobalAdaptivityCalculator = ( GlobalAdaptivityCalculator( self._config, @@ -466,7 +422,6 @@ def initialize(self) -> None: self._comm, ) ) - self._number_of_sims_for_adaptivity = self._global_number_of_sims self._micro_sims_active_steps = np.zeros( self._local_number_of_sims @@ -672,9 +627,7 @@ def _write_data_to_precice(self, data: list) -> None: self._macro_mesh_name, dname, [], np.array([]) ) - def _solve_micro_simulations( - self, micro_sims_input: list, dt: float, adaptivity_data=None - ) -> list: + def _solve_micro_simulations(self, micro_sims_input: list, dt: float) -> list: """ Solve all micro simulations and assemble the micro simulations outputs in a list of dicts format. @@ -685,8 +638,6 @@ def _solve_micro_simulations( solve a micro simulation. dt : float Time step size. - adaptivity_data : list - Dummy parameter to match the signature of the function with the function _solve_micro_simulations_with_adaptivity. Returns ------- @@ -753,10 +704,7 @@ def _solve_micro_simulations( return micro_sims_output def _solve_micro_simulations_with_adaptivity( - self, - micro_sims_input: list, - dt: float, - adaptivity_data: list, + self, micro_sims_input: list, dt: float ) -> list: """ Adaptively solve micro simulations and assemble the micro simulations outputs in a list of dicts format. @@ -768,8 +716,6 @@ def _solve_micro_simulations_with_adaptivity( solve a micro simulation. dt : float Time step size. - adaptivity_data : list - List of numpy arrays: similarity_dists (2D array having similarity distances between each micro simulation pair), is_sim_active (1D array having state (active or inactive) of each micro simulation), sim_is_associated_to (1D array with values of associated simulations of inactive simulations. Active simulations have None) Returns ------- @@ -778,26 +724,19 @@ def _solve_micro_simulations_with_adaptivity( simulations. """ if self._adaptivity_in_every_implicit_step: - adaptivity_data = self._adaptivity_controller.compute_adaptivity( + self._adaptivity_controller.compute_adaptivity( dt, self._micro_sims, - adaptivity_data, self._data_for_adaptivity, ) - active_sim_ids = self._adaptivity_controller.get_active_sim_ids( - adaptivity_data[1] - ) + active_sim_ids = self._adaptivity_controller.get_active_sim_ids() for active_id in active_sim_ids: self._micro_sims_active_steps[active_id] += 1 - active_sim_ids = self._adaptivity_controller.get_active_sim_ids( - adaptivity_data[1] - ) - inactive_sim_ids = self._adaptivity_controller.get_inactive_sim_ids( - adaptivity_data[1] - ) + active_sim_ids = self._adaptivity_controller.get_active_sim_ids() + inactive_sim_ids = self._adaptivity_controller.get_inactive_sim_ids() micro_sims_output = [None] * self._local_number_of_sims @@ -867,7 +806,7 @@ def _solve_micro_simulations_with_adaptivity( ) micro_sims_output = self._adaptivity_controller.get_full_field_micro_output( - adaptivity_data, micro_sims_output + micro_sims_output ) # Resolve micro sim output data for inactive simulations @@ -887,7 +826,7 @@ def _solve_micro_simulations_with_adaptivity( return micro_sims_output - def _get_solve_variant(self) -> Callable[[list, float, list], list]: + def _get_solve_variant(self) -> Callable[[list, float], list]: """ Get the solve variant function based on the adaptivity type. diff --git a/tests/unit/test_adaptivity_parallel.py b/tests/unit/test_adaptivity_parallel.py index 156e847..3db3294 100644 --- a/tests/unit/test_adaptivity_parallel.py +++ b/tests/unit/test_adaptivity_parallel.py @@ -108,6 +108,7 @@ def test_update_all_active_sims_global_adaptivity(self): configurator.get_adaptivity_refining_const = MagicMock(return_value=0.05) configurator.get_adaptivity_coarsening_const = MagicMock(return_value=0.2) configurator.get_adaptivity_similarity_measure = MagicMock(return_value="L2rel") + adaptivity_controller = GlobalAdaptivityCalculator( configurator, 5, global_ids, rank=self._rank, comm=self._comm ) @@ -136,16 +137,20 @@ def get_state(self): for i in global_ids: dummy_micro_sims.append(MicroSimulation(i)) - adaptivity_data = adaptivity_controller.compute_adaptivity( + adaptivity_controller.compute_adaptivity( 0.1, dummy_micro_sims, - adaptivity_data, data_for_adaptivity, ) - self.assertTrue(np.array_equal(expected_is_sim_active, adaptivity_data[1])) self.assertTrue( - np.array_equal(expected_sim_is_associated_to, adaptivity_data[2]) + np.array_equal(expected_is_sim_active, adaptivity_controller._is_sim_active) + ) + self.assertTrue( + np.array_equal( + expected_sim_is_associated_to, + adaptivity_controller._sim_is_associated_to, + ) ) def test_communicate_micro_output(self): @@ -165,11 +170,8 @@ def test_communicate_micro_output(self): sim_output = [output_1, None] expected_sim_output = [output_1, output_0] - adaptivity_data = [] - adaptivity_data.append( - np.array([False, False, True, True, False]) - ) # is_sim_active - adaptivity_data.append([3, 3, -2, -2, 2]) # sim_is_associated_to + is_sim_active = np.array([False, False, True, True, False]) # is_sim_active + sim_is_associated_to = [3, 3, -2, -2, 2] # sim_is_associated_to configurator = MagicMock() configurator.get_adaptivity_similarity_measure = MagicMock(return_value="L1") @@ -177,6 +179,8 @@ def test_communicate_micro_output(self): configurator, 5, global_ids, rank=self._rank, comm=self._comm ) - adaptivity_controller._communicate_micro_output(adaptivity_data, sim_output) + adaptivity_controller._communicate_micro_output( + is_sim_active, sim_is_associated_to, sim_output + ) self.assertTrue(np.array_equal(expected_sim_output, sim_output)) diff --git a/tests/unit/test_adaptivity_serial.py b/tests/unit/test_adaptivity_serial.py index e2ac83d..de9e71b 100644 --- a/tests/unit/test_adaptivity_serial.py +++ b/tests/unit/test_adaptivity_serial.py @@ -68,7 +68,7 @@ def test_get_similarity_dists(self): """ configurator = MagicMock() configurator.get_adaptivity_similarity_measure = MagicMock(return_value="L1") - adaptivity_controller = AdaptivityCalculator(configurator) + adaptivity_controller = AdaptivityCalculator(configurator, 0) adaptivity_controller._hist_param = 0.5 adaptivity_controller._adaptivity_data_names = [ "micro-scalar-data", @@ -102,7 +102,7 @@ def test_update_active_sims(self): """ configurator = MagicMock() configurator.get_adaptivity_similarity_measure = MagicMock(return_value="L1") - adaptivity_controller = AdaptivityCalculator(configurator) + adaptivity_controller = AdaptivityCalculator(configurator, 0) adaptivity_controller._refine_const = self._refine_const adaptivity_controller._coarse_const = self._coarse_const adaptivity_controller._adaptivity_data_names = [ @@ -127,7 +127,7 @@ def test_adaptivity_norms(self): """ Test functionality for calculating similarity criteria between pairs of simulations using different norms in class AdaptivityCalculator. """ - calc = AdaptivityCalculator(Config("micro-manager-config.json")) + calc = AdaptivityCalculator(Config("micro-manager-config.json"), 0) fake_data = np.array([[1], [2], [3]]) self.assertTrue( @@ -208,7 +208,7 @@ def test_associate_active_to_inactive(self): """ configurator = MagicMock() configurator.get_adaptivity_similarity_measure = MagicMock(return_value="L1") - adaptivity_controller = AdaptivityCalculator(configurator) + adaptivity_controller = AdaptivityCalculator(configurator, 0) adaptivity_controller._refine_const = self._refine_const adaptivity_controller._coarse_const = self._coarse_const adaptivity_controller._adaptivity_data_names = [ @@ -235,7 +235,9 @@ def test_update_inactive_sims_local_adaptivity(self): """ configurator = MagicMock() configurator.get_adaptivity_similarity_measure = MagicMock(return_value="L1") - adaptivity_controller = LocalAdaptivityCalculator(configurator, MagicMock()) + adaptivity_controller = LocalAdaptivityCalculator( + configurator, 0, MagicMock(), 5 + ) adaptivity_controller._refine_const = self._refine_const adaptivity_controller._coarse_const = self._coarse_const adaptivity_controller._adaptivity_data_names = [ diff --git a/tests/unit/test_micro_simulation_crash_handling.py b/tests/unit/test_micro_simulation_crash_handling.py index 8e74479..0cdbf9f 100644 --- a/tests/unit/test_micro_simulation_crash_handling.py +++ b/tests/unit/test_micro_simulation_crash_handling.py @@ -98,15 +98,16 @@ def test_crash_handling_with_adaptivity(self): ) manager._micro_sims = [MicroSimulation(i) for i in range(5)] - adaptivity_data = [] - adaptivity_data.append(np.array([0, 0, 0, 0, 0])) # similarity_dists - adaptivity_data.append( - np.array([True, True, True, True, False]) - ) # is_sim_active - adaptivity_data.append(np.array([-2, -2, -2, -2, 2])) # sim_is_associated_to + manager._adaptivity_controller._similarity_dists = np.array([0, 0, 0, 0, 0]) + manager._adaptivity_controller._is_sim_active = np.array( + [True, True, True, True, False] + ) + manager._adaptivity_controller._sim_is_associated_to = np.array( + [-2, -2, -2, -2, 2] + ) micro_sims_output = manager._solve_micro_simulations_with_adaptivity( - macro_data, 1.0, adaptivity_data + macro_data, 1.0 ) # Crashed simulation has interpolated value