Esempio n. 1
0
def print_parent_child_diff(parent, child):
    ps = parent.structure
    cs = child.structure
    slog("PARENT UUID :\t{}".format(parent.uuid))
    slog("CHILD UUID  :\t{}".format(child.uuid))
    slog("lattice constants: (%.2f, %.2f, %.2f) => (%.2f, %.2f, %.2f)" % (ps.a, ps.b, ps.c, cs.a, cs.b, cs.c))
    slog("number of atoms: %.2f => %.2f" % (len(parent.structure.atom_sites),
                                             len(child.structure.atom_sites)))
    parent_ats = ", ".join(["(%.1f, %.1f)" % (ats.epsilon, ats.sigma) for ats in ps.atom_types])
    child_ats = ", ".join(["(%.1f, %.1f)" % (ats.epsilon, ats.sigma) for ats in cs.atom_types])
    slog("atom types: %s => %s" % (parent_ats, child_ats))
Esempio n. 2
0
def parse_output(output_file, material, simulation_config):
    """Parse output file for gas adsorption data.

    Args:
        output_file (str): path to simulation output file.

    Returns:
        results (dict): absolute and excess molar, gravimetric, and volumetric
            gas loadings, as well as energy of average, van der Waals, and
            Coulombic host-host, host-adsorbate, and adsorbate-adsorbate
            interactions.

    """
    gas_loading = GasLoading()
    gas_loading.adsorbate = simulation_config["adsorbate"]
    gas_loading.pressure = simulation_config["pressure"]
    gas_loading.temperature = simulation_config["temperature"]
    atom_blocks = []

    with open(output_file) as origin:
        lines = origin.read().split('\n')
        for i, line in enumerate(lines):

            if "absolute [cm^3 (STP)/c" in line:
                gas_loading.absolute_volumetric_loading = float(
                    line.split()[6])
                gas_loading.absolute_volumetric_loading_error = float(
                    line.split()[8])
            elif "Number of molecules:" in line:
                atom_blocks = [
                    float(lines[offset + i + 5].split()[2])
                    for offset in range(5)
                ]
            elif "Conversion factor molecules/unit cell -> cm^3 STP/cm^3:" in line:
                atoms_uc_to_vv = float(line.split()[7])

        slog("{} LOADING : {} v/v (STP)".format(
            simulation_config["adsorbate"],
            gas_loading.absolute_volumetric_loading))
        if material.parent:
            slog("(parent LOADING : {} v/v (STP))".format(
                material.parent.gas_loading[0].absolute_volumetric_loading))

    return gas_loading, atom_blocks, atoms_uc_to_vv
Esempio n. 3
0
def run_all_simulations(material, config):
    """Simulate helium void fraction, gas loading, and surface area.

    Args:
        material (sqlalchemy.orm.query.Query): material to be analyzed.

    Depending on properties specified in config, adds simulated data for helium
    void fraction, gas loading, heat of adsorption, surface area, and
    corresponding bins to row in database corresponding to the input-material.
    """
    slog("-----------------------------------------------")
    for simulation_number in config["simulations"]:

        simulation_config = config["simulations"][simulation_number]
        slog('Time             : {:%Y-%m-%d %H:%M:%S}'.format(datetime.now()))
        slog("Simulation type  : {}".format(simulation_config["type"]))
        getattr(simulate,
                simulation_config["type"]).run(material, simulation_config,
                                               config)
        slog("--")

    slog('{:%Y-%m-%d %H:%M:%S}'.format(datetime.now()))
Esempio n. 4
0
def run(material, simulation_config, config):
    """Runs void fraction simulation.

    Args:
        material (Material): material record.

    Returns:
        results (dict): void fraction simulation results.

    """
    output_dir = "output_{}_{}".format(material.uuid, uuid4())
    slog("Output directory : {}".format(output_dir))
    os.makedirs(output_dir, exist_ok=True)

    write_output_files(material, simulation_config, output_dir)

    # Run simulations
    slog("Probe            : {}".format(simulation_config["adsorbate"]))
    if "do_geo" in simulation_config:
        slog("Probe radius [geo]: {}".format(simulation_config["probe_radius"]))
    slog("Temperature      : {}".format(simulation_config["temperature"]))

    void_fraction = VoidFraction()
    void_fraction.adsorbate = simulation_config["adsorbate"]
    void_fraction.temperature = simulation_config["temperature"]

    if "do_raspa" in simulation_config and simulation_config["do_raspa"]:
        tbegin = time.perf_counter()
        process = subprocess.run(["simulate", "-i", "./void_fraction.input"], check=True, cwd=output_dir, capture_output=True, text=True)

        data_files = glob(os.path.join(output_dir, "Output", "System_0", "*.data"))
        if len(data_files) != 1:
            raise Exception("ERROR: There should only be one data file in the output directory for %s. Check code!" % output_dir)
        output_file = data_files[0]

        # Parse output
        parse_output(output_file, material, void_fraction)
        slog("RASPA void fraction simulation time: %5.2f seconds" % (time.perf_counter() - tbegin))
        slog("RASPA VOID FRACTION : {}".format(void_fraction.void_fraction))
        if material.parent:
            slog("(parent VOID FRACTION : {})".format(material.parent.void_fraction[0].void_fraction))


    # run geometric void fraction
    if "do_geo" in simulation_config and simulation_config["do_geo"]:
        tbegin = time.perf_counter()
        atoms = [(a.x * material.structure.a, a.y * material.structure.b, a.z * material.structure.c, a.atom_types.sigma) for a in material.structure.atom_sites]
        box = (material.structure.a, material.structure.b, material.structure.c)
        void_fraction.void_fraction_geo = calculate_void_fraction(atoms, box, probe_r=simulation_config["probe_radius"])
        slog("GEOMETRIC void fraction: %f" % void_fraction.void_fraction_geo)
        slog("GEOMETRIC void fraction simulation time: %5.2f   seconds" % (time.perf_counter() - tbegin))
    if "do_zeo" in simulation_config:
        pass
        # run zeo void fraction here

    material.void_fraction.append(void_fraction)

    if not config['keep_configs']:
        shutil.rmtree(output_dir, ignore_errors=True)
    sys.stdout.flush()
Esempio n. 5
0
def mutate_material(parent, config):
    child = parent.clone()
    cs = child.structure

    perturb = set(config["perturb"])
    if config["perturb_type"] == "random":
        child.perturbation = choice(perturb)
        perturb = {child.perturbation}
    else:
        child.perturbation = "all"

    slog("Parent id: %d" % (child.parent_id))
    slog("Perturbing: %s [%s]" % (child.perturbation, perturb))
    ms = config["mutation_strength"]

    if config["number_of_atom_types"] > len(cs.atom_types):
        num_atom_types_to_add = config["number_of_atom_types"] - len(cs.atom_types)
        slog("Adding %d random atom types so we have number defined in the config" % num_atom_types_to_add)
        cs.atom_types += random_atom_types(num_atom_types_to_add, config)

    if perturb & {"num_atoms"} and random() < ms:
        if random() < 0.5: # remove an atoms
            if len(cs.atom_sites) > config['num_atoms_limits'][0]:
                site_to_remove = choice(cs.atom_sites)
                slog("Removing atom site: ", site_to_remove)
                removed_site = cs.atom_sites.remove(site_to_remove)
        else: # add an atom
            if len(cs.atom_sites) < config['num_atoms_limits'][1]:
                slog("Adding atom site...")
                cs.atom_sites += random_atom_sites(1, cs.atom_types)


    if perturb & {"atom_type_assignments"}:
        for i, atom in enumerate(cs.atom_sites):
            if random() < ms**2:
                new_atom_type = choice(cs.atom_types)
                slog("Reassigning atom type for site %d from %d to %d" %
                    (i, cs.atom_types.index(atom.atom_types), cs.atom_types.index(new_atom_type)))
                atom.atom_types = new_atom_type

    if perturb & {"atom_types"}:
        sigl = config["sigma_limits"]
        epsl = config["epsilon_limits"]
        for at in cs.atom_types:
            at.sigma = perturb_unweighted(at.sigma, ms * (sigl[1] - sigl[0]), sigl)
            at.epsilon = perturb_unweighted(at.epsilon, ms * (epsl[1] - epsl[0]), epsl)

    if perturb & {"lattice"}:
        ll = config["lattice_constant_limits"]
        cs.a = perturb_unweighted(cs.a, ms * (ll[1] - ll[0]), ll)
        if config["lattice_cubic"]:
            cs.b = cs.a
            cs.c = cs.a
        else:
            cs.b = perturb_unweighted(cs.b, ms * (ll[1] - ll[0]), ll)
            cs.c = perturb_unweighted(cs.c, ms * (ll[1] - ll[0]), ll)
        child.number_density = len(cs.atom_sites) / cs.volume

    if perturb & {"atom_sites"}:
        for a in cs.atom_sites:
            a.x = random_position(a.x, random(), ms)
            a.y = random_position(a.y, random(), ms)
            a.z = random_position(a.z, random(), ms)

    print_parent_child_diff(parent, child)
    return child
Esempio n. 6
0
def run(material, simulation_config, config):
    """Runs gas loading simulation.

    Args:
        material_id (Material): material record.

    Returns:
        results (dict): gas loading simulation results.

    """
    adsorbate = simulation_config["adsorbate"]
    output_dir = "output_{}_{}".format(material.uuid, uuid4())
    os.makedirs(output_dir, exist_ok=True)
    raspa_config = "./{}_loading.input".format(adsorbate)
    raspa_restart_config = "./{}_loading_restart.input".format(adsorbate)

    # RASPA input-files
    write_output_files(material,
                       simulation_config,
                       output_dir,
                       restart=False,
                       filename=os.path.join(output_dir, raspa_config))
    write_output_files(material,
                       simulation_config,
                       output_dir,
                       restart=True,
                       filename=os.path.join(output_dir, raspa_restart_config))

    # Run simulations
    slog("Adsorbate        : {}".format(adsorbate))
    slog("Pressure         : {}".format(simulation_config["pressure"]))
    slog("Temperature      : {}".format(simulation_config["temperature"]))

    unit_cells = material.structure.minimum_unit_cells(
        simulation_config['cutoff'])
    total_unit_cells = unit_cells[0] * unit_cells[1] * unit_cells[2]
    all_atom_blocks = []

    process = subprocess.run(["simulate", "-i", raspa_config],
                             check=True,
                             cwd=output_dir,
                             capture_output=True,
                             text=True)
    slog(process.stdout)
    for i in range(simulation_config['max_restarts'] + 1):

        data_files = glob(
            os.path.join(output_dir, "Output", "System_0", "*.data"))
        if len(data_files) != 1:
            raise Exception(
                "ERROR: There should only be one data file in the output directory for %s. Check code!"
                % output_dir)
        output_file = data_files[0]

        # Parse output
        gas_loading, atom_blocks, atoms_uc_to_vv = parse_output(
            output_file, material, simulation_config)
        atom_blocks = [
            a * atoms_uc_to_vv / total_unit_cells for a in atom_blocks
        ]
        slog("new blocks for averaging [v/v]: ", atom_blocks)
        slog("atoms_uc_to_vv = %f" % atoms_uc_to_vv)
        slog("reported V/V: %f" % gas_loading.absolute_volumetric_loading)
        slog("reported err: %f" %
             gas_loading.absolute_volumetric_loading_error)

        all_atom_blocks += atom_blocks
        slog("all blocks: ", all_atom_blocks)

        # assign two initialization blocks to every restart run
        run_blocks = all_atom_blocks[math.floor(i / 2) * 5:]
        slog("run blocks: ", run_blocks)
        slog("run blocks len: %f" % (len(run_blocks) / 5))

        blocks_for_averaging = np.mean(np.array(run_blocks).reshape(
            -1, int(len(run_blocks) / 5)),
                                       axis=1)
        slog("incorporated blocks for averaging [v/v]: ", blocks_for_averaging)
        atoms_std = np.std(blocks_for_averaging)
        slog("2*std of all blocks avg %d: %f" % (i, atoms_std * 2))
        slog("2*std of all blocks: %f" % (2 * np.std(run_blocks)))

        error_vv = 2 * atoms_std * atoms_uc_to_vv / total_unit_cells
        gas_loading.absolute_volumetric_loading = np.mean(blocks_for_averaging)
        gas_loading.absolute_volumetric_loading_error = error_vv
        slog("calculated V/V: %f" % gas_loading.absolute_volumetric_loading)
        slog("calculated error: %f" % error_vv)

        slog("Copying restart to RestartInitial...")
        # remove old RestartInitial directory and copy the current one to there
        shutil.rmtree(os.path.join(output_dir, "RestartInitial"),
                      ignore_errors=True)
        shutil.copytree(os.path.join(output_dir, "Restart"),
                        os.path.join(output_dir, "RestartInitial"))

        slog("Moving backup RASPA outputs to restart index")
        shutil.move(os.path.join(output_dir, "Output"),
                    os.path.join(output_dir, "Output-%d" % i))
        shutil.move(os.path.join(output_dir, "Restart"),
                    os.path.join(output_dir, "Restart-%d" % i))
        shutil.move(os.path.join(output_dir, "Movies"),
                    os.path.join(output_dir, "Movies-%d" % i))
        shutil.move(os.path.join(output_dir, "VTK"),
                    os.path.join(output_dir, "VTK-%d" % i))

        gas_loading.cycles = simulation_config['simulation_cycles'] * (i + 1)
        if (gas_loading.absolute_volumetric_loading_error <
                simulation_config['restart_err_threshold']):
            slog(
                "Exiting because v/v err < restart_err_threshold: %4.2f < %4.2f"
                % (gas_loading.absolute_volumetric_loading_error,
                   simulation_config['restart_err_threshold']))
            break
        elif i == simulation_config['max_restarts']:
            slog(
                "Exiting because we've already restarted maximum number of times."
            )
            slog("v/v err >= restart_err_threshold: %4.2f >= %4.2f" %
                 (gas_loading.absolute_volumetric_loading_error,
                  simulation_config['restart_err_threshold']))
            break
        else:
            slog("\n--")
            slog("restart # %d" % i)
            process = subprocess.run(["simulate", "-i", raspa_restart_config],
                                     check=True,
                                     cwd=output_dir,
                                     capture_output=True,
                                     text=True)
            slog(process.stdout)

    material.gas_loading.append(gas_loading)

    if not config['keep_configs']:
        shutil.rmtree(output_dir, ignore_errors=True)
    sys.stdout.flush()