Source code for oasismove.common.io

# Written by Mikael Mortensen <mikaem@math.uio.no> (2013)
# Edited by Henrik Kjeldsberg <henrik.kjeldsberg@live.no> (2023)


import glob
import pickle
import time
from os import listdir, makedirs, path, remove, system
from xml.etree import ElementTree as ET

from dolfin import (
    MPI,
    Function,
    FunctionSpace,
    HDF5File,
    MeshFunction,
    XDMFFile,
    interpolate,
)

from oasismove.problems import info_red

__all__ = [
    "create_initial_folders",
    "save_solution",
    "save_tstep_solution_xdmf",
    "save_checkpoint_solution_xdmf",
    "check_if_kill",
    "check_if_reset_statistics",
    "init_from_restart",
    "merge_visualization_files",
    "merge_xml_files",
]


[docs]def create_initial_folders( folder, restart_folder, sys_comp, tstep, info_red, scalar_components, output_timeseries_as_vector, **NS_namespace ): """Create necessary folders.""" info_red("Creating initial folders") # To avoid writing over old data create a new folder for each run if MPI.rank(MPI.comm_world) == 0: try: makedirs(folder) except OSError: pass MPI.barrier(MPI.comm_world) newfolder = path.join(folder, "data") if restart_folder: newfolder = path.join(newfolder, restart_folder.split("/")[-2]) else: if not path.exists(newfolder): newfolder = path.join(newfolder, "1") else: previous = [f for f in listdir(newfolder) if not f.startswith(".")] previous = max(map(eval, previous)) if previous else 0 newfolder = path.join(newfolder, str(previous + 1)) MPI.barrier(MPI.comm_world) if MPI.rank(MPI.comm_world) == 0: if not restart_folder: makedirs(path.join(newfolder, "Timeseries")) makedirs(path.join(newfolder, "Checkpoint")) tstepfolder = path.join(newfolder, "Timeseries") tstepfiles = {} comps = sys_comp if output_timeseries_as_vector: comps = ["p", "u"] + scalar_components for ui in comps: tstepfiles[ui] = XDMFFile( MPI.comm_world, path.join(tstepfolder, ui + "_from_tstep_{}.xdmf".format(tstep)), ) tstepfiles[ui].parameters["functions_share_mesh"] = True tstepfiles[ui].parameters["rewrite_function_mesh"] = True tstepfiles[ui].parameters["flush_output"] = True return newfolder, tstepfiles
[docs]def save_solution( tstep, t, q_, q_1, w_, d_, folder, newfolder, save_step, checkpoint, NS_parameters, tstepfiles, u_, u_components, output_timeseries_as_vector, mesh, AssignedVectorFunction, killtime, total_timer, **NS_namespace ): """Called at end of timestep. Check for kill and save solution if required.""" NS_parameters.update(t=t, tstep=tstep) if tstep % save_step == 0: save_tstep_solution_xdmf( tstep, q_, u_, newfolder, tstepfiles, output_timeseries_as_vector, AssignedVectorFunction, NS_parameters, ) pauseoasis = check_if_pause(folder) while pauseoasis: time.sleep(5) pauseoasis = check_if_pause(folder) killoasis = check_if_kill(folder, killtime, total_timer) if tstep % checkpoint == 0 or killoasis: save_checkpoint_solution_xdmf( q_, q_1, w_, d_, newfolder, u_components, mesh, NS_parameters ) return killoasis
[docs]def save_tstep_solution_xdmf( tstep, q_, u_, newfolder, tstepfiles, output_timeseries_as_vector, AssignedVectorFunction, NS_parameters, ): """Store solution on current timestep to XDMF file.""" timefolder = path.join(newfolder, "Timeseries") if output_timeseries_as_vector: # project or store velocity to vector function space for comp, tstepfile in tstepfiles.items(): if comp == "u": # Create vector function and assigners uv = AssignedVectorFunction(u_) # Assign solution to vector uv() # Store solution vector tstepfile.write(uv, float(tstep)) elif comp in q_: tstepfile.write(q_[comp], float(tstep)) else: tstepfile.write(tstepfile.function, float(tstep)) else: for comp, tstepfile in tstepfiles.items(): tstepfile << (q_[comp], float(tstep)) if MPI.rank(MPI.comm_world) == 0: if not path.exists(path.join(timefolder, "params.dat")): f = open(path.join(timefolder, "params.dat"), "wb") pickle.dump(NS_parameters, f)
[docs]def save_checkpoint_solution_xdmf( q_, q_1, w_, d_, newfolder, u_components, mesh, NS_parameters ): """ Overwrite solution in Checkpoint folder """ checkpointfolder = path.join(newfolder, "Checkpoint") NS_parameters["num_processes"] = MPI.size(MPI.comm_world) if MPI.rank(MPI.comm_world) == 0: if path.exists(path.join(checkpointfolder, "params.dat")): system( "cp {0} {1}".format( path.join(checkpointfolder, "params.dat"), path.join(checkpointfolder, "params_old.dat"), ) ) f = open(path.join(checkpointfolder, "params.dat"), "wb") pickle.dump(NS_parameters, f) if MPI.rank(MPI.comm_world) == 0 and path.exists( path.join(checkpointfolder, "params_old.dat") ): system("rm {0}".format(path.join(checkpointfolder, "params_old.dat"))) # Store velocity and pressure solution MPI.barrier(MPI.comm_world) for ui in q_: checkpoint_path = path.join(checkpointfolder, ui + ".xdmf") with XDMFFile(MPI.comm_world, checkpoint_path) as f: f.write_checkpoint(q_[ui], "/current") if ui in u_components: f.write_checkpoint(q_1[ui], "/previous", append=True) MPI.barrier(MPI.comm_world) MPI.barrier(MPI.comm_world) # Store mesh velocity and deformation solution if w_ is not None: for ui in w_: checkpoint_path = path.join( checkpointfolder, ui.replace("u", "w") + ".xdmf" ) with XDMFFile(MPI.comm_world, checkpoint_path) as f: f.write_checkpoint(w_[ui], "/current") checkpoint_path = path.join( checkpointfolder, ui.replace("u", "d") + ".xdmf" ) with XDMFFile(MPI.comm_world, checkpoint_path) as f: f.write_checkpoint(d_[ui], "/current") MPI.barrier(MPI.comm_world) # Store mesh and boundary MPI.barrier(MPI.comm_world) mesh_path = path.join(checkpointfolder, "mesh.h5") with HDF5File(MPI.comm_world, mesh_path, "w") as f: f.write(mesh, "mesh") boundary = MeshFunction( "size_t", mesh, mesh.geometry().dim() - 1, mesh.domains() ) f.write(boundary, "boundary")
[docs]def check_if_kill(folder, killtime, total_timer): """Check if user has put a file named killoasis in folder or if given killtime has been reached.""" found = 0 if "killoasis" in listdir(folder): found = 1 collective = MPI.sum(MPI.comm_world, found) if collective > 0: if MPI.rank(MPI.comm_world) == 0: remove(path.join(folder, "killoasis")) info_red("killoasis Found! Stopping simulations cleanly...") return True else: elapsed_time = float(total_timer.elapsed()[0]) if killtime is not None and killtime <= elapsed_time: if MPI.rank(MPI.comm_world) == 0: info_red("Given killtime reached! Stopping simulations cleanly...") return True else: return False
def check_if_pause(folder): """Check if user has put a file named pauseoasis in folder.""" found = 0 if "pauseoasis" in listdir(folder): found = 1 collective = MPI.sum(MPI.comm_world, found) if collective > 0: if MPI.rank(MPI.comm_world) == 0: info_red( "pauseoasis Found! Simulations paused. Remove " + path.join(folder, "pauseoasis") + " to resume simulations..." ) return True else: return False
[docs]def check_if_reset_statistics(folder): """Check if user has put a file named resetoasis in folder.""" found = 0 if "resetoasis" in listdir(folder): found = 1 collective = MPI.sum(MPI.comm_world, found) if collective > 0: if MPI.rank(MPI.comm_world) == 0: remove(path.join(folder, "resetoasis")) info_red("resetoasis Found!") return True else: return False
[docs]def init_from_restart( restart_folder, sys_comp, uc_comp, u_components, q_, q_1, q_2, w_, d_, tstep, velocity_degree, previous_velocity_degree, mesh, constrained_domain, V, Q, **NS_namespace ): """Initialize solution from checkpoint files""" if restart_folder: if MPI.rank(MPI.comm_world) == 0: info_red("Restarting from checkpoint at time step {}".format(tstep)) q_prev = q_ q_2_prev = q_2 if previous_velocity_degree != velocity_degree: # Create dictionaries for the solutions at previous and different element degree V_prev = FunctionSpace( mesh, "CG", previous_velocity_degree, constrained_domain=constrained_domain, ) VV_prev = dict((ui, V_prev) for ui in uc_comp) VV_prev["p"] = Q q_prev = dict((ui, Function(VV_prev[ui], name=ui)) for ui in sys_comp) q_2_prev = dict( (ui, Function(V_prev, name=ui + "_2")) for ui in u_components ) # Load velocity and pressure for ui in sys_comp: checkpoint_path = path.join(restart_folder, ui + ".xdmf") with XDMFFile(MPI.comm_world, checkpoint_path) as f: # Interpolate read_and_interpolate_solution( f, V, previous_velocity_degree, q_, q_prev, ui, velocity_degree, "/current", ) if ui in uc_comp: q_1[ui].vector().zero() q_1[ui].vector().axpy(1.0, q_[ui].vector()) q_1[ui].vector().apply("insert") if ui in u_components: # Interpolate read_and_interpolate_solution( f, V, previous_velocity_degree, q_2, q_2_prev, ui, velocity_degree, "/previous", ) # Load mesh velocity and deformation if w_ is not None: for ui in u_components: checkpoint_path = path.join( restart_folder, ui.replace("u", "w") + ".xdmf" ) with XDMFFile(MPI.comm_world, checkpoint_path) as f: f.read_checkpoint(w_[ui], "/current") checkpoint_path = path.join( restart_folder, ui.replace("u", "d") + ".xdmf" ) with XDMFFile(MPI.comm_world, checkpoint_path) as f: f.read_checkpoint(d_[ui], "/current")
def read_and_interpolate_solution( f, V, previous_velocity_degree, q_, q_prev, ui, velocity_degree, name ): """ Interpolate solution to higher element order or read directly into existing function space """ if previous_velocity_degree != velocity_degree and ui != "p": f.read_checkpoint(q_prev[ui], name) q_prev_proj = interpolate(q_prev[ui], V) q_[ui].vector().zero() q_[ui].vector().axpy(1.0, q_prev_proj.vector()) q_[ui].vector().apply("insert") else: f.read_checkpoint(q_[ui], name)
[docs]def merge_visualization_files(newfolder, **namesapce): timefolder = path.join(newfolder, "Timeseries") # Gather files xdmf_files = list(glob.glob(path.join(timefolder, "*.xdmf"))) xdmf_velocity = [f for f in xdmf_files if "u_from_tstep" in f.__str__()] xdmf_pressure = [f for f in xdmf_files if "p_from_tstep" in f.__str__()] # Merge files for files in [xdmf_velocity, xdmf_pressure]: if len(files) > 1: merge_xml_files(files)
[docs]def merge_xml_files(files): # Get first timestep and trees first_timesteps = [] trees = [] MPI.barrier(MPI.comm_world) for f in files: trees.append(ET.parse(f)) root = trees[-1].getroot() first_timesteps.append(float(root[0][0][0][2].attrib["Value"])) MPI.barrier(MPI.comm_world) # Index valued sort (bypass numpy dependency) first_timestep_sorted = sorted(first_timesteps) indexes = [first_timesteps.index(i) for i in first_timestep_sorted] # Get last timestep of first tree base_tree = trees[indexes[0]] last_node = base_tree.getroot()[0][0][-1] ind = 1 if len(list(last_node)) == 3 else 2 last_timestep = float(last_node[ind].attrib["Value"]) # Append for index in indexes[1:]: tree = trees[index] for node in list(tree.getroot()[0][0]): ind = 1 if len(list(node)) == 3 else 2 if last_timestep < float(node[ind].attrib["Value"]): base_tree.getroot()[0][0].append(node) last_timestep = float(node[ind].attrib["Value"]) # Seperate xdmf files new_file = [f for f in files if "_0" in f] old_files = [f for f in files if "_" in f and f not in new_file] # Write new xdmf file base_tree.write(new_file[0], xml_declaration=True) # Delete xdmf file if MPI.rank(MPI.comm_world) == 0: [remove(f) for f in old_files]