Esempio n. 1
0
def run_strat_sim(set_ICs, name, params):
    snapshots_dir = SNAPSHOTS_DIR % name
    logger = logging.getLogger(name)

    solver, domain = get_solver(params)

    # Initial conditions
    set_ICs(solver, domain, params)
    # filename = '{s}/{s}_s1.h5'.format(s=snapshots_dir)
    # write, dt = solver.load_state(filename, -30)
    # print("=====strat_helper.py=====", "loaded", write)

    cfl = CFL(solver,
              initial_dt=params['DT'],
              cadence=10,
              max_dt=5,
              min_dt=0.01,
              safety=0.5,
              threshold=0.10)
    cfl.add_velocities(('ux', 'uz'))
    cfl.add_frequency(params['DT'])
    snapshots = solver.evaluator.add_file_handler(snapshots_dir,
                                                  sim_dt=params['T_F'] /
                                                  params['NUM_SNAPSHOTS'])
    snapshots.add_system(solver.state)

    # Flow properties
    flow = GlobalFlowProperty(solver, cadence=10)
    flow.add_property("sqrt(ux*ux + uz*uz) / NU", name='Re')

    # Main loop
    logger.info('Starting sim...')
    slices = domain.dist.coeff_layout.slices(scales=1)
    (len_x, len_z) = domain.dist.coeff_layout.global_shape(scales=1)
    # mask kicks in 1/3 of the way, gaussian decay
    len_x_damp = len_x - (len_x // 3)
    len_z_damp = len_z - (len_z // 3)
    x_mask = np.concatenate(
        (np.ones(len_x // 3),
         np.exp(-16 * np.arange(len_x_damp)**2 / len_x_damp**2)))
    z_mask = np.concatenate(
        (np.ones(len_z // 3),
         np.exp(-16 * np.arange(len_z_damp)**2 / len_z_damp**2)))
    mask = np.outer(x_mask, z_mask)
    while solver.ok:
        cfl_dt = cfl.compute_dt() if params.get('USE_CFL') else params['DT']
        solver.step(cfl_dt)
        curr_iter = solver.iteration
        for field in solver.state.fields:
            field['c'] *= mask[slices]

        if curr_iter % int(
            (params['T_F'] / params['DT']) / params['NUM_SNAPSHOTS']) == 0:
            logger.info('Reached time %f out of %f, timestep %f vs max %f',
                        solver.sim_time, solver.stop_sim_time, cfl_dt,
                        params['DT'])
            logger.info('Max Re = %f' % flow.max('Re'))
    def __init__(self, solver_IVP, de_domain_IVP, root_dir, file_dir=None):
        """
        Initialize the FieldAverager.

        Arguments:
        ----------
            solver_IVP : Dedalus Solver object 
                The solver from the IVP in which averages are being taken
            de_domain_IVP : DedalusDomain object
                The domain on which the IVP is being solved
            root_dir : string
                Root output directory
            file_dir : string, optional
                Output average files to root_dir/file_dir. If None, output to root_dir/OUT_DIR.
        """

        self.solver_IVP = solver_IVP
        self.de_domain_IVP = de_domain_IVP

        self.rank = de_domain_IVP.domain.dist.comm_cart.rank
        if len(de_domain_IVP.domain.dist.mesh) == 0:
            self.nz_per_proc = int(de_domain_IVP.resolution[0] /
                                   de_domain_IVP.domain.dist.comm_cart.size)
        else:
            self.nz_per_proc = int(de_domain_IVP.resolution[0] /
                                   de_domain_IVP.domain.dist.mesh[-1])

        self.flow = GlobalFlowProperty(solver_IVP, cadence=1)

        self.measured_profiles, self.avg_profiles, self.local_l2 = OrderedDict(
        ), OrderedDict(), OrderedDict()

        for k, fd in self.FIELDS.items():
            self.flow.add_property('plane_avg({})'.format(fd),
                                   name='{}'.format(k))
            self.measured_profiles[k] = np.zeros((2, self.nz_per_proc))
            self.avg_profiles[k] = np.zeros(self.nz_per_proc)
            self.local_l2[k] = np.zeros(self.nz_per_proc)

        self.avg_times = np.zeros(2)
        self.elapsed_avg_time = 0

        self.n_files_saved = 0
        if file_dir is None:
            file_dir = self.OUT_DIR
        self.file_dir = '{:s}/{:s}/'.format(root_dir, file_dir)
        mpi_makedirs(self.file_dir)
Esempio n. 3
0
def run_strat_sim(set_ICs, name, params):
    snapshots_dir = SNAPSHOTS_DIR % name
    filename = '{s}/{s}_s1.h5'.format(s=snapshots_dir)
    logger = logging.getLogger(name)

    solver, domain = get_solver(params)

    # Initial conditions
    dt = params['DT']
    _, dt = set_ICs(name, solver, domain, params)

    cfl = CFL(solver,
              initial_dt=dt,
              cadence=10,
              max_dt=params['DT'],
              min_dt=0.01,
              safety=0.5,
              threshold=0.10)
    cfl.add_velocities(('ux', 'uz'))
    cfl.add_frequency(params['DT'])
    snapshots = solver.evaluator.add_file_handler(snapshots_dir,
                                                  sim_dt=params['T_F'] /
                                                  params['NUM_SNAPSHOTS'])
    snapshots.add_system(solver.state)

    # Flow properties
    flow = GlobalFlowProperty(solver, cadence=10)
    flow.add_property('sqrt((ux / NU)**2 + (uz / NU)**2)', name='Re')

    # Main loop
    logger.info('Starting sim...')
    while solver.ok:
        cfl_dt = cfl.compute_dt() if params.get('USE_CFL') else params['DT']
        # if cfl_dt < params['DT'] / 8: # small step sizes if strongly cfl limited
        #     cfl_dt /= 2
        solver.step(cfl_dt)
        curr_iter = solver.iteration

        if curr_iter % int(
            (params['T_F'] / params['DT']) / params['NUM_SNAPSHOTS']) == 0:
            logger.info('Reached time %f out of %f, timestep %f vs max %f',
                        solver.sim_time, solver.stop_sim_time, cfl_dt,
                        params['DT'])
            logger.info('Max Re = %f' % flow.max('Re'))
Esempio n. 4
0
def run_strat_sim(name, params):
    populate_globals(params)
    snapshots_dir = SNAPSHOTS_DIR % name

    solver, domain = get_solver(params)

    # Initial conditions
    _, dt = set_ic(name, solver, domain, params)

    cfl = CFL(solver,
              initial_dt=dt,
              cadence=5,
              max_dt=DT,
              min_dt=0.007,
              safety=0.5,
              threshold=0.10)
    cfl.add_velocities(('ux', 'uz'))
    cfl.add_frequency(DT)
    snapshots = solver.evaluator.add_file_handler(snapshots_dir,
                                                  sim_dt=T_F / NUM_SNAPSHOTS)
    snapshots.add_system(solver.state)
    snapshots.add_task("integ(RHO0 * ux * uz, 'x') / XMAX", name='S_{px}')

    # Flow properties
    flow = GlobalFlowProperty(solver, cadence=10)
    flow.add_property('sqrt(ux**2 + ux**2)', name='u')

    # Main loop
    logger.info('Starting sim...')
    while solver.ok:
        cfl_dt = cfl.compute_dt() if NL else DT
        solver.step(cfl_dt)
        curr_iter = solver.iteration

        if curr_iter % int((T_F / DT) / NUM_SNAPSHOTS) == 0:
            logger.info('Reached time %f out of %f, timestep %f vs max %f',
                        solver.sim_time, solver.stop_sim_time, cfl_dt, DT)
            logger.info('Max u = %e' % flow.max('u'))
Esempio n. 5
0
    def __init__(self,
                 nz,
                 solver_ivp,
                 dist_ivp,
                 ae_fields,
                 extra_fields,
                 P,
                 R,
                 first_ae_wait_time=30,
                 ae_wait_time=20,
                 first_ae_avg_time=20,
                 ae_avg_time=10,
                 first_ae_avg_thresh=1e-2,
                 ae_avg_thresh=1e-3,
                 ivp_convergence_thresh=1e-2):
        """
        Initialize the object by grabbing solver states and making room for profile averages
        
        Arguments: 
        ----------
        All arguments have identical descriptions to their description in the class level docstring.
        """
        self.ivp_convergence_thresh = ivp_convergence_thresh
        self.first_ae_wait_time = self.curr_ae_wait_time = first_ae_wait_time
        self.first_ae_avg_time = self.curr_ae_avg_time = first_ae_avg_time
        self.first_ae_avg_thresh = self.curr_ae_avg_thresh = first_ae_avg_thresh
        self.ae_wait_time = ae_wait_time
        self.ae_avg_time = ae_avg_time
        self.ae_avg_thresh = ae_avg_thresh
        self.nz = nz
        self.solver_ivp = solver_ivp
        self.dist_ivp = dist_ivp
        self.ae_fields = ae_fields
        self.extra_fields = extra_fields
        self.P, self.R = P, R
        self.doing_ae, self.finished_ae, self.pe_switch = False, False, False

        #Set up profiles and simulation tracking
        self.flow = GlobalFlowProperty(solver_ivp, cadence=1)
        self.z_slices = self.dist_ivp.grid_layout.slices(scales=1)[-1]
        self.nz_per_proc = self.dist_ivp.grid_layout.local_shape(scales=1)[-1]
        self.measured_times = np.zeros(2)
        self.elapsed_avg_time = 0
        self.measured_profiles, self.avg_profiles, self.local_l2 = OrderedDict(
        ), OrderedDict(), OrderedDict()
        for k in ae_fields:
            self.flow.add_property('plane_avg({})'.format(k),
                                   name='{}'.format(k))
            self.measured_profiles[k] = np.zeros((2, self.nz_per_proc))
            self.avg_profiles[k] = np.zeros(self.nz_per_proc)
            self.local_l2[k] = np.zeros(self.nz_per_proc)
        for k in extra_fields:
            self.flow.add_property('plane_avg({})'.format(k),
                                   name='{}'.format(k))
        self.flow.add_property('Pe', name='Pe')

        if self.dist_ivp.comm_cart.rank == 0:
            self.AE_basis = de.Chebyshev('z',
                                         self.nz,
                                         interval=[-1. / 2, 1. / 2],
                                         dealias=3. / 2)
            self.AE_domain = de.Domain([
                self.AE_basis,
            ],
                                       grid_dtype=np.float64,
                                       comm=MPI.COMM_SELF)
        else:
            self.AE_basis = self.AE_domain = None
Esempio n. 6
0
class BoussinesqAESolver:
    """
    Logic for coupling into a Dedalus IVP solve of Boussinesq, Rayleigh-Benard Convection.
    Currently only implemented for mixed thermal boundary conditions (fixed-flux at bottom,
    fixed-temp at top).

    Attributes:
    -----------
    (first_/curr_)ae_wait_time: float
        Freefall times to wait before collecting averages on (first/current) AE solve
    (first_/curr_)ae_avg_time: float
        Minimum number of freefall times over which to take averages on (first/current) AE solve
    (first_/curr_)ae_avg_thresh: float
        Convergence criterion, in % diff, of profiles before (first/current) AE BVP solve triggers
    AE_basis : Dedalus Chebyshev
        1D z-basis for AE solve
    AE_domain : Dedalus Domain
        Domain object for AE solve
    ae_fields : list
        Strings of variables whose profiles must converge on AE solves
    avg_profiles : OrderedDict
        Contains time-averaged profiles
    dist_ivp : Dedalus Distributor
        MPI distributor from convection DNS
    doing_ae : bool
        If True, profiles are actively being averaged for an AE solve
    elapsed_avg_time : float
        Total sim time that has passed over the averaging window
    extra_fields : list
        Fields to track the instantaneous vertical profile of from the DNS.
    finished_ae : bool
        If True, AE has converged within ivp_convergence_thresh
    flow : Dedalus GlobalFlowProperty
        Tracks instantaneous profiles in the DNS
    ivp_convergence_thresh: float
        When AE changes the current simulation's temp profile by this % or lower, consider solution converged.
    local_l2 : OrderedDict
        Contains info about change in profile from previous to current average.
    measured_profiles : OrderedDict
        Contains info about the current and previous instantaneous profile values
    measured_times : NumPy Array
        Sim time at measurement of current and previous profile values
    nz : int
        Chebyshev coefficient resolution
    nz_per_proc : int
        z data points in grid space on the local processor
    P, R : floats
        The values of 1/sqrt(Rayleigh*Prandtl) and 1/sqrt(Rayleigh/Prandtl)
    pe_switch : bool
        True if the avg Pe in the sim grid is > 1
    solver_ivp : Dedalus InitialValueSolver
        Solver object from convection DNS
    z_slices : list
        z indices of the local processor
    """
    def __init__(self,
                 nz,
                 solver_ivp,
                 dist_ivp,
                 ae_fields,
                 extra_fields,
                 P,
                 R,
                 first_ae_wait_time=30,
                 ae_wait_time=20,
                 first_ae_avg_time=20,
                 ae_avg_time=10,
                 first_ae_avg_thresh=1e-2,
                 ae_avg_thresh=1e-3,
                 ivp_convergence_thresh=1e-2):
        """
        Initialize the object by grabbing solver states and making room for profile averages
        
        Arguments: 
        ----------
        All arguments have identical descriptions to their description in the class level docstring.
        """
        self.ivp_convergence_thresh = ivp_convergence_thresh
        self.first_ae_wait_time = self.curr_ae_wait_time = first_ae_wait_time
        self.first_ae_avg_time = self.curr_ae_avg_time = first_ae_avg_time
        self.first_ae_avg_thresh = self.curr_ae_avg_thresh = first_ae_avg_thresh
        self.ae_wait_time = ae_wait_time
        self.ae_avg_time = ae_avg_time
        self.ae_avg_thresh = ae_avg_thresh
        self.nz = nz
        self.solver_ivp = solver_ivp
        self.dist_ivp = dist_ivp
        self.ae_fields = ae_fields
        self.extra_fields = extra_fields
        self.P, self.R = P, R
        self.doing_ae, self.finished_ae, self.pe_switch = False, False, False

        #Set up profiles and simulation tracking
        self.flow = GlobalFlowProperty(solver_ivp, cadence=1)
        self.z_slices = self.dist_ivp.grid_layout.slices(scales=1)[-1]
        self.nz_per_proc = self.dist_ivp.grid_layout.local_shape(scales=1)[-1]
        self.measured_times = np.zeros(2)
        self.elapsed_avg_time = 0
        self.measured_profiles, self.avg_profiles, self.local_l2 = OrderedDict(
        ), OrderedDict(), OrderedDict()
        for k in ae_fields:
            self.flow.add_property('plane_avg({})'.format(k),
                                   name='{}'.format(k))
            self.measured_profiles[k] = np.zeros((2, self.nz_per_proc))
            self.avg_profiles[k] = np.zeros(self.nz_per_proc)
            self.local_l2[k] = np.zeros(self.nz_per_proc)
        for k in extra_fields:
            self.flow.add_property('plane_avg({})'.format(k),
                                   name='{}'.format(k))
        self.flow.add_property('Pe', name='Pe')

        if self.dist_ivp.comm_cart.rank == 0:
            self.AE_basis = de.Chebyshev('z',
                                         self.nz,
                                         interval=[-1. / 2, 1. / 2],
                                         dealias=3. / 2)
            self.AE_domain = de.Domain([
                self.AE_basis,
            ],
                                       grid_dtype=np.float64,
                                       comm=MPI.COMM_SELF)
        else:
            self.AE_basis = self.AE_domain = None

    def _broadcast_ae_solution(self, solver):
        """ Communicate AE solve info from process 0 to all processes """
        base_keys = ['T1', 'Xi', 'T1_z']
        ae_profiles = OrderedDict()
        for f in base_keys:
            ae_profiles[f] = np.zeros(self.nz)
        for f in ['Xi_mean', 'delta_T1']:
            ae_profiles[f] = np.zeros(1)

        if self.dist_ivp.comm_cart.rank == 0:
            for f in base_keys:
                state = solver.state[f]
                state.set_scales(1, keep_data=True)
                ae_profiles[f] = np.copy(state['g'])
            ae_profiles['Xi_mean'] = np.mean(
                solver.state['Xi'].integrate()['g'])
            ae_profiles['delta_T1'] = np.mean(solver.state['delta_T1']['g'])

        for f in ae_profiles.keys():
            ae_profiles[f] = self.dist_ivp.comm_cart.bcast(ae_profiles[f],
                                                           root=0)
        return ae_profiles

    def _check_convergence(self):
        """Check if each field in self.ae_fields is converged, return True if so."""
        if (self.solver_ivp.sim_time -
                self.curr_ae_wait_time) > self.curr_ae_avg_time:
            maxs = list()
            for f in self.ae_fields:
                maxs.append(self._get_profile_max(self.local_l2[f]))

            logger.info(
                'AE: Max abs L2 norm for convergence: {:.4e} / {:.4e}'.format(
                    np.median(maxs), self.curr_ae_avg_thresh))
            if np.median(maxs) < self.curr_ae_avg_thresh:
                return True
            else:
                return False

    def _communicate_profile(self, profile):
        """
        Construct and return a global z-profile using local pieces.

        Arguments:
        ----------
            profile : NumPy array
                contains the local piece of the profile
        """
        loc, glob = [np.zeros(self.nz) for i in range(2)]
        if len(self.dist_ivp.mesh) == 0:
            loc[self.z_slices] = profile
        elif self.dist_ivp.comm_cart.rank < self.dist_ivp.mesh[-1]:
            loc[self.z_slices] = profile
        self.dist_ivp.comm_cart.Allreduce(loc, glob, op=MPI.SUM)
        return glob

    def _get_local_profile(self, key):
        """
        Grab the specified profile from the DNS and return it as a 1D array.

        Arguments:
        ----------
            prof_name: string
                The name of the profile to grab.
        """
        this_field = self.flow.properties['{}'.format(key)]['g']
        if len(this_field.shape) == 3:
            profile = this_field[0, 0, :]
        else:
            profile = this_field[0, :]
        return profile

    def _get_profile_max(self, profile):
        """
        Return a profile's global maximum; utilize local pieces and communication.

        Arguments:
        ----------
            profile : NumPy array
                contains the local piece of a profile
        """
        loc, glob = [np.zeros(1) for i in range(2)]
        if len(self.dist_ivp.mesh) == 0:
            loc[0] = np.max(profile)
        elif self.dist_ivp.comm_cart.rank < self.dist_ivp.mesh[-1]:
            loc[0] = np.max(profile)
        self.dist_ivp.comm_cart.Allreduce(loc, glob, op=MPI.MAX)
        return glob[0]

    def _reset_profiles(self):
        """ Reset all local fields after doing a BVP """
        for fd in self.ae_fields:
            self.avg_profiles[fd] *= 0
            self.measured_profiles[fd] *= 0
            self.local_l2[fd] *= 0
            self.measured_times *= 0
        self.measured_times[1] = self.solver_ivp.sim_time
        self.elapsed_avg_time = 0

    def _set_AE_equations(self, problem):
        """ Set the horizontally-averaged boussinesq equations """
        problem.add_equation("Xi = (P/tot_flux)")
        problem.add_equation("delta_T1 = left(T1) - right(T1)")

        problem.add_equation("dz(T1) - T1_z = 0")
        problem.add_equation(("P*dz(T1_z) = dz(Xi*enth_flux)"))
        problem.add_equation(("dz(p) - T1 = Xi*momentum_rhs_z"))

        problem.add_bc("left(T1_z) = 0")
        problem.add_bc("right(T1) = 0")
        problem.add_bc('right(p) = 0')

    def _update_avg_profiles(self):
        """ Update the averages of tracked z-profiles. """
        first = False

        # times
        self.measured_times[0] = self.solver_ivp.sim_time
        this_dt = self.measured_times[0] - self.measured_times[1]
        if self.elapsed_avg_time == 0:
            first = True
        self.elapsed_avg_time += this_dt
        self.measured_times[1] = self.measured_times[0]

        # profiles
        for k in self.ae_fields:
            self.measured_profiles[k][0, :] = self._get_local_profile(k)
            if first:
                self.avg_profiles[k] *= 0
                self.local_l2[k] *= 0
            else:
                old_avg = self.avg_profiles[k] / (self.elapsed_avg_time -
                                                  this_dt)
                self.avg_profiles[k] += (this_dt / 2) * np.sum(
                    self.measured_profiles[k], axis=0)
                new_avg = self.avg_profiles[k] / self.elapsed_avg_time
                self.local_l2[k] = np.abs((new_avg - old_avg) / new_avg)
            self.measured_profiles[k][1, :] = self.measured_profiles[k][0, :]

    def _update_simulation_state(self, ae_profiles, avg_fields):
        """ Update T1 and T1_z with AE profiles """
        u_scaling = ae_profiles['Xi_mean']**(1. / 3)
        thermo_scaling = u_scaling**(2)

        #Get instantaneous thermo profiles
        [
            self.flow.properties[f].set_scales(1, keep_data=True)
            for f in ('T1', 'delta_T1')
        ]
        T1_prof = self.flow.properties['T1']['g']
        old_delta_T1 = np.mean(self.flow.properties['delta_T1']['g'])
        new_delta_T1 = ae_profiles['delta_T1']

        T1 = self.solver_ivp.state['T1']
        T1_z = self.solver_ivp.state['T1_z']

        #Adjust Temp
        T1.set_scales(1, keep_data=True)
        T1['g'] -= T1_prof
        T1.set_scales(1, keep_data=True)
        T1['g'] *= thermo_scaling
        T1.set_scales(1, keep_data=True)
        T1['g'] += ae_profiles['T1'][self.z_slices]
        T1.set_scales(1, keep_data=True)
        T1.differentiate('z', out=self.solver_ivp.state['T1_z'])

        #Adjust velocity
        vel_fields = ['u', 'w']
        if len(T1['g'].shape) == 3:
            vel_fields.append('v')
        for k in vel_fields:
            self.solver_ivp.state[k].set_scales(1, keep_data=True)
            self.solver_ivp.state[k]['g'] *= u_scaling

        #See how much delta T over domain has changed.
        diff = np.mean(np.abs(1 - new_delta_T1 / old_delta_T1))
        return diff

    def loop_tasks(self, tolerance=1e-10):
        """ 
        Perform AE tasks every loop iteration.

        Arguments:
        ---------
        tolerance : float
            convergence tolerance for AE NLBVP
        """
        # Don't do anything AE related if Pe < 1
        if self.flow.grid_average('Pe') < 1 and not self.pe_switch:
            return
        elif not self.pe_switch:
            self.curr_ae_wait_time += self.solver_ivp.sim_time
            self.pe_switch = True

        #If first averaging iteration, reset stuff properly
        first = False
        if not self.doing_ae and not self.finished_ae and self.solver_ivp.sim_time >= self.curr_ae_wait_time:
            self._reset_profiles()  #set time data properly
            self.doing_ae = True
            first = True

        if self.doing_ae:
            self._update_avg_profiles()
            if first: return

            do_AE = self._check_convergence()
            if do_AE:
                #Get averages from global domain
                avg_fields = OrderedDict()
                for k, prof in self.avg_profiles.items():
                    avg_fields[k] = self._communicate_profile(
                        prof / self.elapsed_avg_time)

                #Solve BVP
                if self.dist_ivp.comm_cart.rank == 0:
                    problem = de.NLBVP(
                        self.AE_domain,
                        variables=['T1', 'T1_z', 'p', 'delta_T1', 'Xi'])
                    for k, p in (['P', self.P], ['R', self.R]):
                        problem.parameters[k] = p
                    for k, p in avg_fields.items():
                        f = self.AE_domain.new_field()
                        f['g'] = p
                        problem.parameters[k] = f
                    self._set_AE_equations(problem)
                    solver = problem.build_solver()
                    pert = solver.perturbations.data
                    pert.fill(1 + tolerance)
                    while np.sum(np.abs(pert)) > tolerance:
                        solver.newton_iteration()
                        logger.info('Perturbation norm: {}'.format(
                            np.sum(np.abs(pert))))
                else:
                    solver = None

                # Update fields appropriately
                ae_structure = self._broadcast_ae_solution(solver)
                diff = self._update_simulation_state(ae_structure, avg_fields)

                #communicate diff
                if diff < self.ivp_convergence_thresh: self.finished_ae = True
                logger.info('Diff: {:.4e}, finished_ae? {}'.format(
                    diff, self.finished_ae))
                self.doing_ae = False
                self.curr_ae_wait_time = self.solver_ivp.sim_time + self.ae_wait_time
                self.curr_ae_avg_time = self.ae_avg_time
                self.curr_ae_avg_thresh = self.ae_avg_thresh
class FieldAverager:
    """ Does averaging for accelerated evolution. WIP. """

    FIELDS = None
    OUT_DIR = 'averager'

    def __init__(self, solver_IVP, de_domain_IVP, root_dir, file_dir=None):
        """
        Initialize the FieldAverager.

        Arguments:
        ----------
            solver_IVP : Dedalus Solver object 
                The solver from the IVP in which averages are being taken
            de_domain_IVP : DedalusDomain object
                The domain on which the IVP is being solved
            root_dir : string
                Root output directory
            file_dir : string, optional
                Output average files to root_dir/file_dir. If None, output to root_dir/OUT_DIR.
        """

        self.solver_IVP = solver_IVP
        self.de_domain_IVP = de_domain_IVP

        self.rank = de_domain_IVP.domain.dist.comm_cart.rank
        if len(de_domain_IVP.domain.dist.mesh) == 0:
            self.nz_per_proc = int(de_domain_IVP.resolution[0] /
                                   de_domain_IVP.domain.dist.comm_cart.size)
        else:
            self.nz_per_proc = int(de_domain_IVP.resolution[0] /
                                   de_domain_IVP.domain.dist.mesh[-1])

        self.flow = GlobalFlowProperty(solver_IVP, cadence=1)

        self.measured_profiles, self.avg_profiles, self.local_l2 = OrderedDict(
        ), OrderedDict(), OrderedDict()

        for k, fd in self.FIELDS.items():
            self.flow.add_property('plane_avg({})'.format(fd),
                                   name='{}'.format(k))
            self.measured_profiles[k] = np.zeros((2, self.nz_per_proc))
            self.avg_profiles[k] = np.zeros(self.nz_per_proc)
            self.local_l2[k] = np.zeros(self.nz_per_proc)

        self.avg_times = np.zeros(2)
        self.elapsed_avg_time = 0

        self.n_files_saved = 0
        if file_dir is None:
            file_dir = self.OUT_DIR
        self.file_dir = '{:s}/{:s}/'.format(root_dir, file_dir)
        mpi_makedirs(self.file_dir)

    def get_local_profile(self, prof_name):
        """
        Grabs one of the local flow tracker's profiles. Assumes no horizontal variation of profile.

        Arguments:
        ----------
            prof_name: string
                The name of the profile to grab.
        """
        this_field = self.flow.properties['{}'.format(prof_name)]['g']
        if self.de_domain_IVP.dimensions == 3:
            profile = this_field[0, 0, :]
        else:
            profile = this_field[0, :]
        return profile

    def local_to_global_average(self, profile):
        """
        Given the local piece of a dedalus z-profile, find the global z-profile.

        Arguments:
        ----------
            profile : NumPy array
                contains the local piece of the profile
        """
        loc, glob = [
            np.zeros(self.de_domain_IVP.resolution[0]) for i in range(2)
        ]
        if len(self.de_domain_IVP.domain.dist.mesh) == 0:
            loc[self.nz_per_proc * self.rank:self.nz_per_proc *
                (self.rank + 1)] = profile
        elif self.rank < self.de_domain_IVP.domain.dist.mesh[-1]:
            loc[self.nz_per_proc * self.rank:self.nz_per_proc *
                (self.rank + 1)] = profile
        self.de_domain_IVP.domain.dist.comm_cart.Allreduce(loc,
                                                           glob,
                                                           op=MPI.SUM)
        return glob

    def find_global_max(self, profile):
        """
        Given a local piece of a global profile, find the global maximum.

        Arguments:
        ----------
            profile : NumPy array
                contains the local piece of a profile
        """
        loc, glob = [np.zeros(1) for i in range(2)]
        if len(self.de_domain_IVP.domain.dist.mesh) == 0:
            loc[0] = np.max(profile)
        elif self.rank < self.de_domain_IVP.domain.dist.mesh[-1]:
            loc[0] = np.max(profile)
        self.de_domain_IVP.domain.dist.comm_cart.Allreduce(loc,
                                                           glob,
                                                           op=MPI.MAX)
        return glob[0]

    def update_avgs(self):
        """
        Updates the averages of z-profiles. To be called every timestep.
        """
        first = False

        #Times
        self.avg_times[0] = self.solver_IVP.sim_time
        this_dt = self.avg_times[0] - self.avg_times[1]
        if self.elapsed_avg_time == 0:
            first = True
        self.elapsed_avg_time += this_dt
        self.avg_times[1] = self.avg_times[0]

        #Profiles
        for k in self.FIELDS.keys():
            self.measured_profiles[k][0, :] = self.get_local_profile(k)
            if first:
                self.avg_profiles[k] *= 0
                self.local_l2[k] *= 0
            else:
                old_avg = self.avg_profiles[k] / (self.elapsed_avg_time -
                                                  this_dt)
                self.avg_profiles[k] += (this_dt / 2) * np.sum(
                    self.measured_profiles[k], axis=0)
                new_avg = self.avg_profiles[k] / self.elapsed_avg_time
                self.local_l2[k] = np.abs((new_avg - old_avg) / new_avg)

            self.measured_profiles[k][1, :] = self.measured_profiles[k][0, :]

    def save_file(self):
        """  Saves profiles dict to file """
        z_profile = self.local_to_global_average(
            self.de_domain_IVP.z.flatten())
        profiles = OrderedDict()
        for k, item in self.avg_profiles.items():
            profiles[k] = self.local_to_global_average(item /
                                                       self.elapsed_avg_time)

        if self.rank == 0:
            file_name = self.file_dir + "profile_dict_file_{:04d}.h5".format(
                self.n_files_saved + 1)
            with h5py.File(file_name, 'w') as f:
                for k, item in profiles.items():
                    f[k] = item
                f['z'] = z_profile
        self.n_files_saved += 1

    def reset_fields(self):
        """ Reset all local fields after doing a BVP """
        for fd, info in self.FIELDS.items():
            self.avg_profiles[fd] *= 0
            self.measured_profiles[fd] *= 0
            self.local_l2[fd] *= 0
            self.avg_times *= 0
        self.avg_times[1] = self.solver_IVP.sim_time
        self.elapsed_avg_time = 0
Esempio n. 8
0
        dt = DT

    cfl = CFL(solver,
              initial_dt=dt,
              cadence=5,
              max_dt=DT,
              min_dt=0.007,
              safety=0.5,
              threshold=0.10)
    cfl.add_velocities(('ux', 'uz'))
    cfl.add_frequency(DT)
    snapshots = solver.evaluator.add_file_handler(snapshots_dir,
                                                  sim_dt=T_F / NUM_SNAPSHOTS)
    snapshots.add_system(solver.state)

    # Flow properties
    flow = GlobalFlowProperty(solver, cadence=10)
    flow.add_property('sqrt(ux**2 + ux**2)', name='u')

    # Main loop
    logger.info('Starting sim...')
    while solver.ok:
        cfl_dt = cfl.compute_dt()
        solver.step(cfl_dt)
        curr_iter = solver.iteration

        if curr_iter % int((T_F / DT) / NUM_SNAPSHOTS) == 0:
            logger.info('Reached time %f out of %f, timestep %f vs max %f',
                        solver.sim_time, solver.stop_sim_time, cfl_dt, DT)
            logger.info('Max u = %e' % flow.max('u'))