From 7ce6b34b478fbd4f834f442d4e82a258a95f54cd Mon Sep 17 00:00:00 2001 From: Ishaan Desai Date: Tue, 2 Apr 2024 15:24:59 +0200 Subject: [PATCH] Fix initialize() call of MicroSimulation class when adaptivity is off. Also fix formatter --- .github/workflows/check-pep8.yml | 22 --- .pre-commit-config.yaml | 9 +- micro_manager/micro_manager.py | 300 ++++++++++++++++++++++--------- 3 files changed, 220 insertions(+), 111 deletions(-) delete mode 100644 .github/workflows/check-pep8.yml diff --git a/.github/workflows/check-pep8.yml b/.github/workflows/check-pep8.yml deleted file mode 100644 index 46ef5313..00000000 --- a/.github/workflows/check-pep8.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: autopep8 -on: - push: - branches: - - main - - develop - pull_request: - branches: - - "*" -jobs: - autopep8: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: autopep8 - id: autopep8 - uses: peter-evans/autopep8@v1 - with: - args: --recursive --diff --aggressive --aggressive --exit-code --ignore E402 --max-line-length 120 . - - name: Fail if autopep8 made changes - if: ${{ steps.autopep8.outputs.exit-code == 2 }} - run: exit 1 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3a121152..fdc41dbd 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -7,12 +7,11 @@ repos: - id: mixed-line-ending - id: end-of-file-fixer - id: trailing-whitespace -# autopep8 for Python formatting -- repo: https://github.com/hhatto/autopep8 - rev: v2.1.0 +# black repo for python formatting +- repo: https://github.com/ambv/black + rev: 22.12.0 hooks: - - id: autopep8 - args: [--recursive --diff --aggressive --aggressive --exit-code --ignore E402 --max-line-length 120 .] + - id: black # isort for python imports - repo: https://github.com/pycqa/isort rev: 5.12.0 diff --git a/micro_manager/micro_manager.py b/micro_manager/micro_manager.py index 704e8196..5d7089df 100644 --- a/micro_manager/micro_manager.py +++ b/micro_manager/micro_manager.py @@ -12,23 +12,24 @@ """ import argparse +import importlib +import logging import os import sys -import precice -from mpi4py import MPI -import numpy as np -import logging import time -import importlib from copy import deepcopy from typing import Dict from warnings import warn -from .config import Config -from .micro_simulation import create_simulation_class -from .adaptivity.local_adaptivity import LocalAdaptivityCalculator +import numpy as np +import precice +from mpi4py import MPI + from .adaptivity.global_adaptivity import GlobalAdaptivityCalculator +from .adaptivity.local_adaptivity import LocalAdaptivityCalculator +from .config import Config from .domain_decomposition import DomainDecomposer +from .micro_simulation import create_simulation_class sys.path.append(os.getcwd()) @@ -51,11 +52,13 @@ def __init__(self, config_file: str) -> None: self._logger.setLevel(level=logging.INFO) # Create file handler which logs messages - fh = logging.FileHandler('micro-manager.log') + fh = logging.FileHandler("micro-manager.log") fh.setLevel(logging.INFO) # Create formatter and add it to handlers - formatter = logging.Formatter('[' + str(self._rank) + '] %(name)s - %(levelname)s - %(message)s') + formatter = logging.Formatter( + "[" + str(self._rank) + "] %(name)s - %(levelname)s - %(message)s" + ) fh.setFormatter(formatter) self._logger.addHandler(fh) # add the handlers to the logger @@ -67,10 +70,8 @@ def __init__(self, config_file: str) -> None: # Define the preCICE Participant self._participant = precice.Participant( - "Micro-Manager", - self._config.get_config_file_name(), - self._rank, - self._size) + "Micro-Manager", self._config.get_config_file_name(), self._rank, self._size + ) self._macro_mesh_name = self._config.get_macro_mesh_name() @@ -114,7 +115,9 @@ def __init__(self, config_file: str) -> None: if name in self._write_data_names: self._adaptivity_micro_data_names[name] = is_data_vector - self._adaptivity_in_every_implicit_step = self._config.is_adaptivity_required_in_every_implicit_iteration() + self._adaptivity_in_every_implicit_step = ( + self._config.is_adaptivity_required_in_every_implicit_iteration() + ) self._micro_sims_active_steps = None self._initialize() @@ -139,20 +142,35 @@ def solve(self) -> None: if self._is_adaptivity_on: similarity_dists = np.zeros( - (self._number_of_sims_for_adaptivity, - self._number_of_sims_for_adaptivity)) + ( + self._number_of_sims_for_adaptivity, + self._number_of_sims_for_adaptivity, + ) + ) # Start adaptivity calculation with all sims active is_sim_active = np.array([True] * self._number_of_sims_for_adaptivity) # Active sims do not have an associated sim - sim_is_associated_to = np.full((self._number_of_sims_for_adaptivity), -2, dtype=np.intc) + sim_is_associated_to = np.full( + (self._number_of_sims_for_adaptivity), -2, dtype=np.intc + ) # If micro simulations have been initialized, compute adaptivity based on initial data if self._micro_sims_init: # Compute adaptivity based on initial data of micro sims - similarity_dists, is_sim_active, sim_is_associated_to = self._adaptivity_controller.compute_adaptivity( - self._dt, self._micro_sims, similarity_dists, is_sim_active, sim_is_associated_to, self._data_for_adaptivity) + ( + similarity_dists, + is_sim_active, + sim_is_associated_to, + ) = self._adaptivity_controller.compute_adaptivity( + self._dt, + self._micro_sims, + similarity_dists, + is_sim_active, + sim_is_associated_to, + self._data_for_adaptivity, + ) while self._participant.is_coupling_ongoing(): # Write a checkpoint @@ -164,8 +182,18 @@ def solve(self) -> None: if self._is_adaptivity_on: if not self._adaptivity_in_every_implicit_step: - similarity_dists, is_sim_active, sim_is_associated_to = self._adaptivity_controller.compute_adaptivity( - self._dt, self._micro_sims, similarity_dists, is_sim_active, sim_is_associated_to, self._data_for_adaptivity) + ( + similarity_dists, + is_sim_active, + sim_is_associated_to, + ) = self._adaptivity_controller.compute_adaptivity( + self._dt, + self._micro_sims, + similarity_dists, + is_sim_active, + sim_is_associated_to, + self._data_for_adaptivity, + ) # Only checkpoint the adaptivity configuration if adaptivity is computed # once in every time window @@ -177,7 +205,13 @@ def solve(self) -> None: active_sim_ids = np.where(is_sim_active)[0] elif self._adaptivity_type == "global": active_sim_ids = np.where( - is_sim_active[self._global_ids_of_local_sims[0]:self._global_ids_of_local_sims[-1] + 1])[0] + is_sim_active[ + self._global_ids_of_local_sims[ + 0 + ] : self._global_ids_of_local_sims[-1] + + 1 + ] + )[0] for active_id in active_sim_ids: self._micro_sims_active_steps[active_id] += 1 @@ -186,20 +220,37 @@ def solve(self) -> None: if self._is_adaptivity_on: if self._adaptivity_in_every_implicit_step: - similarity_dists, is_sim_active, sim_is_associated_to = self._adaptivity_controller.compute_adaptivity( - self._dt, self._micro_sims, similarity_dists, is_sim_active, sim_is_associated_to, self._data_for_adaptivity) + ( + similarity_dists, + is_sim_active, + sim_is_associated_to, + ) = self._adaptivity_controller.compute_adaptivity( + self._dt, + self._micro_sims, + similarity_dists, + is_sim_active, + sim_is_associated_to, + self._data_for_adaptivity, + ) if self._adaptivity_type == "local": active_sim_ids = np.where(is_sim_active)[0] elif self._adaptivity_type == "global": active_sim_ids = np.where( - is_sim_active[self._global_ids_of_local_sims[0]:self._global_ids_of_local_sims[-1] + 1])[0] + is_sim_active[ + self._global_ids_of_local_sims[ + 0 + ] : self._global_ids_of_local_sims[-1] + + 1 + ] + )[0] for active_id in active_sim_ids: self._micro_sims_active_steps[active_id] += 1 micro_sims_output = self._solve_micro_simulations_with_adaptivity( - micro_sims_input, is_sim_active, sim_is_associated_to) + micro_sims_input, is_sim_active, sim_is_associated_to + ) else: micro_sims_output = self._solve_micro_simulations(micro_sims_input) @@ -226,8 +277,13 @@ def solve(self) -> None: sim_is_associated_to = np.copy(sim_is_associated_to_cp) else: # Time window has converged, now micro output can be generated - self._logger.info("Micro simulations {} - {} have converged at t = {}".format( - self._micro_sims[0].get_global_id(), self._micro_sims[-1].get_global_id(), t)) + self._logger.info( + "Micro simulations {} - {} have converged at t = {}".format( + self._micro_sims[0].get_global_id(), + self._micro_sims[-1].get_global_id(), + t, + ) + ) if self._micro_sims_have_output: if n % self._micro_n_out == 0: @@ -251,31 +307,46 @@ def _initialize(self) -> None: """ # Decompose the macro-domain and set the mesh access region for each partition in preCICE assert len(self._macro_bounds) / 2 == self._participant.get_mesh_dimensions( - self._macro_mesh_name), "Provided macro mesh bounds are of incorrect dimension" + self._macro_mesh_name + ), "Provided macro mesh bounds are of incorrect dimension" if self._is_parallel: domain_decomposer = DomainDecomposer( - self._logger, self._participant.get_mesh_dimensions(self._macro_mesh_name), self._rank, self._size) - coupling_mesh_bounds = domain_decomposer.decompose_macro_domain(self._macro_bounds, self._ranks_per_axis) + self._logger, + self._participant.get_mesh_dimensions(self._macro_mesh_name), + self._rank, + self._size, + ) + coupling_mesh_bounds = domain_decomposer.decompose_macro_domain( + self._macro_bounds, self._ranks_per_axis + ) else: coupling_mesh_bounds = self._macro_bounds - self._participant.set_mesh_access_region(self._macro_mesh_name, coupling_mesh_bounds) + self._participant.set_mesh_access_region( + self._macro_mesh_name, coupling_mesh_bounds + ) # initialize preCICE self._participant.initialize() - self._mesh_vertex_ids, mesh_vertex_coords = self._participant.get_mesh_vertex_ids_and_coordinates( - self._macro_mesh_name) - assert (mesh_vertex_coords.size != 0), "Macro mesh has no vertices." + ( + self._mesh_vertex_ids, + mesh_vertex_coords, + ) = self._participant.get_mesh_vertex_ids_and_coordinates(self._macro_mesh_name) + assert mesh_vertex_coords.size != 0, "Macro mesh has no vertices." self._local_number_of_sims, _ = mesh_vertex_coords.shape - self._logger.info("Number of local micro simulations = {}".format(self._local_number_of_sims)) + self._logger.info( + "Number of local micro simulations = {}".format(self._local_number_of_sims) + ) if self._local_number_of_sims == 0: if self._is_parallel: self._logger.info( "Rank {} has no micro simulations and hence will not do any computation.".format( - self._rank)) + self._rank + ) + ) self._is_rank_empty = True else: raise Exception("Micro Manager has no micro simulations.") @@ -293,13 +364,20 @@ def _initialize(self) -> None: for name, is_data_vector in self._adaptivity_data_names.items(): if is_data_vector: self._data_for_adaptivity[name] = np.zeros( - (self._local_number_of_sims, self._participant.get_data_dimensions( - self._macro_mesh_name, name))) + ( + self._local_number_of_sims, + self._participant.get_data_dimensions( + self._macro_mesh_name, name + ), + ) + ) else: - self._data_for_adaptivity[name] = np.zeros((self._local_number_of_sims)) + self._data_for_adaptivity[name] = np.zeros( + (self._local_number_of_sims) + ) # Create lists of local and global IDs - sim_id = np.sum(nms_all_ranks[:self._rank]) + sim_id = np.sum(nms_all_ranks[: self._rank]) self._global_ids_of_local_sims = [] # DECLARATION for i in range(self._local_number_of_sims): self._global_ids_of_local_sims.append(sim_id) @@ -309,22 +387,28 @@ def _initialize(self) -> None: micro_problem = getattr( importlib.import_module( - self._config.get_micro_file_name(), - "MicroSimulation"), - "MicroSimulation") + self._config.get_micro_file_name(), "MicroSimulation" + ), + "MicroSimulation", + ) # Create micro simulation objects for i in range(self._local_number_of_sims): - self._micro_sims[i] = create_simulation_class( - micro_problem)(self._global_ids_of_local_sims[i]) + self._micro_sims[i] = create_simulation_class(micro_problem)( + self._global_ids_of_local_sims[i] + ) - self._logger.info("Micro simulations with global IDs {} - {} created.".format( - self._global_ids_of_local_sims[0], self._global_ids_of_local_sims[-1])) + self._logger.info( + "Micro simulations with global IDs {} - {} created.".format( + self._global_ids_of_local_sims[0], self._global_ids_of_local_sims[-1] + ) + ) if self._is_adaptivity_on: if self._adaptivity_type == "local": self._adaptivity_controller = LocalAdaptivityCalculator( - self._config, self._logger) + self._config, self._logger + ) self._number_of_sims_for_adaptivity = self._local_number_of_sims elif self._adaptivity_type == "global": self._adaptivity_controller = GlobalAdaptivityCalculator( @@ -333,7 +417,8 @@ def _initialize(self) -> None: self._global_number_of_sims, self._global_ids_of_local_sims, self._rank, - self._comm) + 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) @@ -341,15 +426,25 @@ def _initialize(self) -> None: self._micro_sims_init = False # DECLARATION # Get initial data from micro simulations if initialize() method exists - if hasattr(micro_problem, 'initialize') and callable(getattr(micro_problem, 'initialize')): - if self._is_adaptivity_on: - self._micro_sims_init = True - initial_micro_output = self._micro_sims[0].initialize() # Call initialize() of the first simulation - if initial_micro_output is None: # Check if the detected initialize() method returns any data - warn("The initialize() call of the Micro simulation has not returned any initial data." - " The initialize call is stopped.") - self._micro_sims_init = False - else: + if hasattr(micro_problem, "initialize") and callable( + getattr(micro_problem, "initialize") + ): + self._micro_sims_init = True + initial_micro_output = self._micro_sims[ + 0 + ].initialize() # Call initialize() of the first simulation + if ( + initial_micro_output is None + ): # Check if the detected initialize() method returns any data + warn( + "The initialize() call of the Micro simulation has not returned any initial data." + " This means that the initialize() call has no effect on the adaptivity." + ) + self._micro_sims_init = False + for i in range(1, self._local_number_of_sims): + self._micro_sims[i].initialize() + else: + if self._is_adaptivity_on: # Save initial data from first micro simulation as we anyway have it for name in initial_micro_output.keys(): self._data_for_adaptivity[name][0] = initial_micro_output[name] @@ -358,13 +453,17 @@ def _initialize(self) -> None: for i in range(1, self._local_number_of_sims): initial_micro_output = self._micro_sims[i].initialize() for name in self._adaptivity_micro_data_names: - self._data_for_adaptivity[name][i] = initial_micro_output[name] - else: - self._logger.info( - "Micro simulation has the method initialize(), but it is not called, because adaptivity is off.") + self._data_for_adaptivity[name][i] = initial_micro_output[ + name + ] + else: + for i in range(1, self._local_number_of_sims): + self._micro_sims[i].initialize() self._micro_sims_have_output = False - if hasattr(micro_problem, 'output') and callable(getattr(micro_problem, 'output')): + if hasattr(micro_problem, "output") and callable( + getattr(micro_problem, "output") + ): self._micro_sims_have_output = True self._dt = self._participant.get_max_time_step_size() @@ -383,8 +482,13 @@ def _read_data_from_precice(self) -> list: read_data[name] = [] for name in self._read_data_names.keys(): - read_data.update({name: self._participant.read_data( - self._macro_mesh_name, name, self._mesh_vertex_ids, self._dt)}) + read_data.update( + { + name: self._participant.read_data( + self._macro_mesh_name, name, self._mesh_vertex_ids, self._dt + ) + } + ) if self._is_adaptivity_on: if name in self._adaptivity_macro_data_names: @@ -412,10 +516,16 @@ def _write_data_to_precice(self, data: list) -> None: for dname in self._write_data_names.keys(): self._participant.write_data( - self._macro_mesh_name, dname, self._mesh_vertex_ids, data_dict[dname]) + self._macro_mesh_name, + dname, + self._mesh_vertex_ids, + data_dict[dname], + ) else: for dname in self._write_data_names.keys(): - self._participant.write_data(self._macro_mesh_name, dname, [], np.array([])) + self._participant.write_data( + self._macro_mesh_name, dname, [], np.array([]) + ) def _solve_micro_simulations(self, micro_sims_input: list) -> list: """ @@ -446,10 +556,11 @@ def _solve_micro_simulations(self, micro_sims_input: list) -> list: return micro_sims_output def _solve_micro_simulations_with_adaptivity( - self, - micro_sims_input: list, - is_sim_active: np.ndarray, - sim_is_associated_to: np.ndarray) -> list: + self, + micro_sims_input: list, + is_sim_active: np.ndarray, + sim_is_associated_to: np.ndarray, + ) -> list: """ Solve all micro simulations and assemble the micro simulations outputs in a list of dicts format. @@ -471,9 +582,22 @@ def _solve_micro_simulations_with_adaptivity( """ if self._adaptivity_type == "global": active_sim_ids = np.where( - is_sim_active[self._global_ids_of_local_sims[0]:self._global_ids_of_local_sims[-1] + 1])[0] + is_sim_active[ + self._global_ids_of_local_sims[0] : self._global_ids_of_local_sims[ + -1 + ] + + 1 + ] + )[0] inactive_sim_ids = np.where( - is_sim_active[self._global_ids_of_local_sims[0]:self._global_ids_of_local_sims[-1] + 1] == False)[0] + is_sim_active[ + self._global_ids_of_local_sims[0] : self._global_ids_of_local_sims[ + -1 + ] + + 1 + ] + == False + )[0] elif self._adaptivity_type == "local": active_sim_ids = np.where(is_sim_active)[0] inactive_sim_ids = np.where(is_sim_active == False)[0] @@ -483,28 +607,37 @@ def _solve_micro_simulations_with_adaptivity( # Solve all active micro simulations for active_id in active_sim_ids: start_time = time.time() - micro_sims_output[active_id] = self._micro_sims[active_id].solve(micro_sims_input[active_id], self._dt) + micro_sims_output[active_id] = self._micro_sims[active_id].solve( + micro_sims_input[active_id], self._dt + ) end_time = time.time() # Mark the micro sim as active for export micro_sims_output[active_id]["active_state"] = 1 - micro_sims_output[active_id]["active_steps"] = self._micro_sims_active_steps[active_id] + micro_sims_output[active_id][ + "active_steps" + ] = self._micro_sims_active_steps[active_id] if self._is_micro_solve_time_required: micro_sims_output[active_id]["micro_sim_time"] = end_time - start_time # For each inactive simulation, copy data from most similar active simulation if self._adaptivity_type == "global": - self._adaptivity_controller.communicate_micro_output(is_sim_active, sim_is_associated_to, micro_sims_output) + self._adaptivity_controller.communicate_micro_output( + is_sim_active, sim_is_associated_to, micro_sims_output + ) elif self._adaptivity_type == "local": 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[sim_is_associated_to[inactive_id]] + ) # Resolve micro sim output data for inactive simulations for inactive_id in inactive_sim_ids: micro_sims_output[inactive_id]["active_state"] = 0 - micro_sims_output[inactive_id]["active_steps"] = self._micro_sims_active_steps[inactive_id] + micro_sims_output[inactive_id][ + "active_steps" + ] = self._micro_sims_active_steps[inactive_id] if self._is_micro_solve_time_required: micro_sims_output[inactive_id]["micro_sim_time"] = 0 @@ -518,11 +651,10 @@ def _solve_micro_simulations_with_adaptivity( def main(): - parser = argparse.ArgumentParser(description='.') + parser = argparse.ArgumentParser(description=".") parser.add_argument( - 'config_file', - type=str, - help='Path to the JSON config file of the manager.') + "config_file", type=str, help="Path to the JSON config file of the manager." + ) args = parser.parse_args() config_file_path = args.config_file