profile
viewpoint
If you are wondering where the data of this site comes from, please visit https://api.github.com/users/bocklund/events. GitMemory does not store any data, but only uses NGINX to cache data for a period of time. The idea behind GitMemory is simply to give users a better reading experience.
Brandon Bocklund bocklund Penn State State College, PA brandonbocklund.com Materials science and engineering graduate student at the Pennsylvania State University

bocklund/Calphad.jl 2

This repository is an experimental implementation of pycalphad-inspired Calphad package rewritten in Julia.

bocklund/pycalphad 1

CALPHAD tools for designing thermodynamic models, calculating phase diagrams and investigating phase equilibria.

bocklund/tinydb 1

TinyDB is a lightweight document oriented database optimized for your happiness :)

bocklund/atomate 0

atomate is a powerful software for computational materials science and contains pre-built workflows.

bocklund/conda-forge-pinning-feedstock 0

A conda-smithy repository for conda-forge-pinning.

bocklund/conda-forge.github.io 0

Documentation for conda-forge

bocklund/cyipopt 0

Cython interface for the interior point optimzer IPOPT

bocklund/cyipopt-feedstock 0

A conda-smithy repository for cyipopt.

Pull request review commentpycalphad/pycalphad

ENH: Add metastable composition sets to starting point

 def _solve_eq_at_conditions(properties, phase_records, grid, conds_keys, state_v             composition_sets.append(compset)         chemical_potentials = prop_MU_values[it.multi_index]         energy = prop_GM_values[it.multi_index]+        add_nearly_stable(composition_sets, phase_records, grid, curr_idx, chemical_potentials,

Following up on the previous comment, can we replace add_new_phases by add_nearly_stable if we slightly modify the behavior of add_nearly_stable to add a small amount of a phase with positive driving force (like the 1e-6 we use in add_new_phases)?

richardotis

comment created time in 5 hours

PullRequestReviewEvent

Pull request review commentpycalphad/pycalphad

ENH: Add metastable composition sets to starting point

 cdef bint add_new_phases(object composition_sets, object removed_compsets, objec         return True     return False +@cython.boundscheck(False)+cdef int argmax(double* a, int a_shape) nogil:+    cdef int i+    cdef int result = 0+    cdef double highest = -1e30+    for i in range(a_shape):+        if a[i] > highest:+            highest = a[i]+            result = i+    return result++def add_nearly_stable(object composition_sets, dict phase_records,

Does it make any difference for this to be cdef or cpdef?

richardotis

comment created time in 5 hours

PullRequestReviewEvent

Pull request review commentpycalphad/pycalphad

ENH: Add metastable composition sets to starting point

 def _solve_eq_at_conditions(properties, phase_records, grid, conds_keys, state_v             composition_sets.append(compset)         chemical_potentials = prop_MU_values[it.multi_index]         energy = prop_GM_values[it.multi_index]+        add_nearly_stable(composition_sets, phase_records, grid, curr_idx, chemical_potentials,

Does it make sense that the nearly stable composition sets have one shot to make it into the solution? After the first call to find_solution, all the remaining metastable composition sets will be removed if find_solution converged and the nearly metastable composition sets won't be able to make it in.

Since we have more metastable composition sets in the solution, does it make sense to remove the metastable composition sets less aggressively after find_solution, waiting until we think we found the global min to remove the metastable candidates?

richardotis

comment created time in 5 hours

Pull request review commentpycalphad/pycalphad

ENH: Add metastable composition sets to starting point

 cdef bint add_new_phases(object composition_sets, object removed_compsets, objec         return True     return False +@cython.boundscheck(False)+cdef int argmax(double* a, int a_shape) nogil:+    cdef int i+    cdef int result = 0+    cdef double highest = -1e30+    for i in range(a_shape):+        if a[i] > highest:+            highest = a[i]+            result = i+    return result++def add_nearly_stable(object composition_sets, dict phase_records,+                      object grid, object current_idx, np.ndarray[ndim=1, dtype=np.float64_t] chemical_potentials,+                      double[::1] state_variables, double minimum_df, bint verbose):+    cdef double[::1] driving_forces, driving_forces_for_phase+    cdef double[:,::1] current_grid_Y = grid.Y[*current_idx, ...]+    cdef double[:,::1] current_grid_X = grid.X[*current_idx, ...]+    cdef double[::1] current_grid_GM = grid.GM[*current_idx, ...]+    cdef unicode phase_name+    cdef CompositionSet compset = composition_sets[0]+    cdef set stable_phases = {compset.phase_record.phase_name for compset in composition_sets}+    cdef PhaseRecord phase_record+    cdef int num_statevars = len(compset.phase_record.state_variables)+    cdef int df_idx, minimum_df_idx+    cdef bint phases_added = False+    driving_forces = np.dot(current_grid_X, chemical_potentials) - current_grid_GM

Does it make more sense to allocate driving forces outside add_nearly_stable and pass the array to the out keyword argument of np.dot? For broadcasted equilibrium calculations, this will allocate an array of the same size as grid.GM for every set of conditions, but the size of the grid doesn't change.

Profiling would reveal if it's worth doing.

richardotis

comment created time in 5 hours

delete branch bocklund/pycalphad

delete branch : phase-recs-api

delete time in 2 days

push eventpycalphad/pycalphad

Brandon Bocklund

commit sha e9c5d6deeb04cd3dea1324ea7419f3ab92041b21

ENH: Allow passing dictionaries of PhaseRecord objects to equilibrium and calculate (#361) - `build_phase_records` - Defaults to build derivatives of the output. This is the more common case and SymEngine makes the compilation fast enough that's it's not as much of a burden to build them by default. - The `comps` argument now accepts string components or `Species` objects via `unpack_components`. Before this change, only `Species` objects were allowed. - The `conds` argument now just requires an iterable (including dicts), so condition dictionaries or lists/sets/tuples of state variables can be passed through a tweak of `pycalphad.core.utils.get_state_variables` - `calculate` and `equilibrium` - Accept the `phase_records` keyword argument - The `callables` keyword argument is used if the phase records are constructed. If the `phase_records` argument is supplied by the caller, the `callables` argument is ignored. - `equilibrium` passes `phase_records` down to `calculate` instead of rebuilding them in `calculate` - Both functions check that all the active phases have entries in `phase_records` and raise an error if not. We currently don't check that the `output` is correct at all - This approach is relatively simple, but one gap is that there's no mechanism to pass `phase_records` in to `equilibrium` that get re-used by `_eqcalculate`. `calculate` will always build `phase_records` when called from `_eqcalculate` (the `callables` can still be passed down for now).

view details

push time in 2 days

PR merged pycalphad/pycalphad

ENH: Allow passing dictionaries of PhaseRecord objects to equilibrium and calculate

Changes:

  • build_phase_records
    • Defaults to build derivatives of the output. This is the more common case and SymEngine makes the compilation fast enough that's it's not as much of a burden to build them by default.
    • The comps argument now accepts string components or Species objects via unpack_components. Before this change, only Species objects were allowed.
  • calculate and equilibrium
    • Accept the phase_records keyword argument
    • The callables keyword argument is used if the phase records are constructed. If the phase_records argument is supplied by the caller, the callables argument is ignored.
    • equilibrium passes phase_records down to calculate instead of rebuilding them in calculate
    • Both functions check that all the active phases have entries in phase_records and raise an error if not. We currently don't check that the output is correct at all
  • This approach is relatively simple, but one gap is that there's no mechanism to pass phase_records in to equilibrium that get re-used by _eqcalculate. calculate will always build phase_records when called from _eqcalculate (the callables can still be passed down for now).
+145 -33

2 comments

7 changed files

bocklund

pr closed time in 2 days

PullRequestReviewEvent

Pull request review commentpycalphad/pycalphad

ENH: Allow passing dictionaries of PhaseRecord objects to equilibrium and calculate

 def test_single_model_instance_raises():     mod = Model(DBF, comps, 'L12_FCC')  # Model instance does not match the phase     with pytest.raises(ValueError):         calculate(DBF, comps, ['LIQUID', 'L12_FCC'], T=1400.0, output='_fail_', model=mod)+++def test_missing_phase_records_passed_to_calculate_raises():+    "calculate should raise an error if all the active phases are not included in the phase_records"+    my_phases = ['LIQUID', 'FCC_A1']+    subset_phases = ['FCC_A1']+    comps = ['AL', 'FE', 'VA']++    models = instantiate_models(ALFE_DBF, comps, subset_phases)+    # Dummy conditions are just needed to get all the state variables into the PhaseRecord objects+    phase_records = build_phase_records(ALFE_DBF, comps, subset_phases, {v.N: None, v.P: None, v.T: None}, models)

I changed the name of the conds argument of build_phase_records to state_variables, so that covers the semantic change.

For the implementation, I made a slight modification to get_state_variables to loop over conds directly instead of accessing conds.keys(). With this modification, we can take advantage of the fact that iterating over Python dictionaries means iterating over the keys. Thus, passing dictionaries of conditions to state_variables is backwards compatible and passing any iterable (list, set, etc.) of state variables also works.

bocklund

comment created time in 2 days

push eventbocklund/pycalphad

bocklund

commit sha 854cf4e4951e7596d1f6a20d2bdc55253ebb6ca8

build_phase_records take state_variables instead of conds

view details

push time in 2 days

pull request commentconda-forge/phonopy-feedstock

Add myself as maintainer

FYI: the conda-forge bot can help open PRs to add yourself as a maintainer and not run CI: https://conda-forge.org/docs/maintainer/updating_pkgs.html#updating-the-maintainer-list

jan-janssen

comment created time in 5 days

PullRequestReviewEvent

pull request commentPhasesResearchLab/dfttk

pull request for merging changes on Debye model

@yiwang62 please feel free to re-request a review via the GitHub UI when you are finished making changes

yiwang62

comment created time in 7 days

Pull request review commentpycalphad/pycalphad

ENH: Allow passing dictionaries of PhaseRecord objects to equilibrium and calculate

 def test_eq_binary():     eqx = equilibrium(ALFE_DBF, comps, my_phases, conds, verbose=True)     assert_allclose(eqx.GM.values.flat[0], -9.608807e4) ++def test_phase_records_passed_to_equilibrium():+    "Pre-built phase records can be passed to equilibrium."+    my_phases = ['LIQUID', 'FCC_A1', 'HCP_A3', 'AL5FE2', 'AL2FE', 'AL13FE4', 'AL5FE4']+    comps = ['AL', 'FE', 'VA']+    conds = {v.T: 1400, v.P: 101325, v.N: 1.0, v.X('AL'): 0.55}++    models = instantiate_models(ALFE_DBF, comps, my_phases)+    phase_records = build_phase_records(ALFE_DBF, comps, my_phases, conds, models)++    # Without models passed+    eqx = equilibrium(ALFE_DBF, comps, my_phases, conds, verbose=True, phase_records=phase_records)

Here's the commit that adds code to handle this behavior in the spirit of the first option: https://github.com/pycalphad/pycalphad/pull/361/commits/a13cbcc6a3453ba415a1fee3ea72b07c137fae70

It's safer, but doesn't really protect users from providing incompatible model instances. The user is still responsible for making sure their argument to model is actually correct. Model objects really only serve two purposes in the equilibrium code path:

  1. building phase records
  2. getting the size of the internal degrees of freedom and the indices of pure vacancy endmembers for point sampling

if we could somehow do the second one without a Model instance, we could make it so providing a phase record or model are mutually exclusive per phase.

bocklund

comment created time in 13 days

PullRequestReviewEvent

push eventbocklund/pycalphad

bocklund

commit sha a13cbcc6a3453ba415a1fee3ea72b07c137fae70

Fix test argument

view details

push time in 13 days

push eventbocklund/pycalphad

bocklund

commit sha 51a8d4a75b94a15bc535cdc068a63f87bb0f3c04

Require models to be specified if phase records are specified

view details

push time in 13 days

Pull request review commentpycalphad/pycalphad

ENH: Allow passing dictionaries of PhaseRecord objects to equilibrium and calculate

 def test_eq_binary():     eqx = equilibrium(ALFE_DBF, comps, my_phases, conds, verbose=True)     assert_allclose(eqx.GM.values.flat[0], -9.608807e4) ++def test_phase_records_passed_to_equilibrium():+    "Pre-built phase records can be passed to equilibrium."+    my_phases = ['LIQUID', 'FCC_A1', 'HCP_A3', 'AL5FE2', 'AL2FE', 'AL13FE4', 'AL5FE4']+    comps = ['AL', 'FE', 'VA']+    conds = {v.T: 1400, v.P: 101325, v.N: 1.0, v.X('AL'): 0.55}++    models = instantiate_models(ALFE_DBF, comps, my_phases)+    phase_records = build_phase_records(ALFE_DBF, comps, my_phases, conds, models)++    # Without models passed+    eqx = equilibrium(ALFE_DBF, comps, my_phases, conds, verbose=True, phase_records=phase_records)

I agree. I'm leaning towards (a) because I don't want to enforce coupling between phase records and models.

One complication is that the model argument can be unpacked via unpack_kwarg and it can have many forms as input, so it may be tricky to validate. Some options of how to handle it in the case that phase_records is passed:

  1. Do not instantiate_models, but make a best effort check that all(isinstance(mod, Model) for mod in model.values()). This alone wouldn't catch the case where the model argument only overrides a subset of active phases (and instantiate_models would typically build the rest), but we could address that in a similar way.
  2. Do not instantiate models. Don't do any validation and just pass whatever the model argument was to build_phase_records, letting build_phase_records and build_callables raise appropriately.
bocklund

comment created time in 14 days

PullRequestReviewEvent

Pull request review commentpycalphad/pycalphad

ENH: Allow passing dictionaries of PhaseRecord objects to equilibrium and calculate

 def test_single_model_instance_raises():     mod = Model(DBF, comps, 'L12_FCC')  # Model instance does not match the phase     with pytest.raises(ValueError):         calculate(DBF, comps, ['LIQUID', 'L12_FCC'], T=1400.0, output='_fail_', model=mod)+++def test_missing_phase_records_passed_to_calculate_raises():+    "calculate should raise an error if all the active phases are not included in the phase_records"+    my_phases = ['LIQUID', 'FCC_A1']+    subset_phases = ['FCC_A1']+    comps = ['AL', 'FE', 'VA']++    models = instantiate_models(ALFE_DBF, comps, subset_phases)+    # Dummy conditions are just needed to get all the state variables into the PhaseRecord objects+    phase_records = build_phase_records(ALFE_DBF, comps, subset_phases, {v.N: None, v.P: None, v.T: None}, models)

I'm in favor of accepting a sequence of state variables here. Our next release also changes some of the internal solver APIs, so we might as well feel free to change others as well.

bocklund

comment created time in 14 days

PullRequestReviewEvent

pull request commentpycalphad/pycalphad

ENH: Allow passing dictionaries of PhaseRecord objects to equilibrium and calculate

Calling equilibrium in tight loops could be really expensive because the time to continually rebuild the phase records. After making the changes in this PR, I was able to call build_phase_records in the scheil package and pass them to equilibrium and see huge boost in performance, see these profiles of a single simulation. The benchmark images below show a reduction from 491 seconds -> 8 seconds by eliminating the redundant calls to build_phase_records every time equilibrium is called:

Before: passing the callables from build_callables to equilibrium (491 seconds, 95% of time in build_phase_records called 1204 times)

image

After: passing the phase records from build_phase_records to equilibrium (8.6 seconds, 0.9 seconds of time in build_phase_records called one time)

image

Why didn't we do this before?

Before the new minimizer was implemented, the choice of external conditions was tightly coupled with PhaseRecord objects via Model.get_multiphase_constraints. That is, changing the conditions for an equilibrium calculation in an A-B binary system from N, P, T, X(B) to N, P, T, X(A) required a new set of phase records. This tight coupling was what led to passing around callables, which only depended on the state variables and not the external conditions. Since the conditions in the new minimizer are implemented inside the minimizer itself, each PhaseRecord now only needs to know about the state variables and the internal constraints for that phase.

bocklund

comment created time in 14 days

PR opened pycalphad/pycalphad

ENH: Allow passing dictionaries of PhaseRecord objects to equilibrium and calculate

Changes:

  • build_phase_records
    • Defaults to build derivatives of the output. This is the more common case and SymEngine makes the compilation fast enough that's it's not as much of a burden to build them by default.
    • The comps argument now accepts string components or Species objects via unpack_components. Before this change, only Species objects were allowed.
  • calculate and equilibrium
    • Accept the phase_records keyword argument
    • The callables keyword argument is used if the phase records are constructed. If the phase_records argument is supplied by the caller, the callables argument is ignored.
    • equilibrium passes phase_records down to calculate instead of rebuilding them in calculate
    • Both functions check that all the active phases have entries in phase_records and raise an error if not. We currently don't check that the output is correct at all
  • This approach is relatively simple, but one gap is that there's no mechanism to pass phase_records in to equilibrium that get re-used by _eqcalculate. calculate will always build phase_records when called from _eqcalculate (the callables can still be passed down for now).
+89 -17

0 comment

5 changed files

pr created time in 14 days

create barnchbocklund/pycalphad

branch : phase-recs-api

created branch time in 14 days

delete branch bocklund/pycalphad

delete branch : minimizer-no-copy-state

delete time in 14 days

push eventpycalphad/pycalphad

Brandon Bocklund

commit sha c1e613398fda41c6a7ddfd36ae86c4fa61c9b99f

ENH: Refactor solver and improve solver performance (#360) Performance-based refactoring leading to 2x performance improvement. Most changes were semantic or numeric, but the possible changes to correctness are: - the step size in `find_solution` is now hardcoded to `1.0`. Variable steps in Δy are still enabled. - `compute_phase_matrix` has the terms removed for subtracting a contribution of the `chemical_potential*mass_hessian`, which I think is not correct according to the equations in Sundman's 2015 paper. - `change_phases` had code added to obey Gibbs phase rule - `remove_and_consolidate_phases` now ensures that any consolidated phases have some non-zero phase amount to fix a bug where the total number of moles could become zero if consolidated phases had equal and opposite phase amounts and all the other phases became unstable in the same iteration.

view details

push time in 14 days

PR merged pycalphad/pycalphad

ENH: Refactor solver and improve solver performance

I have noticed in benchmarking that roughly 1/3 of the time in the solver is spent performing this copy operation on the state. We don't do anything meaningful with the old state except for using its data, so I propose to remove it and replace the couple instances with explicitly storing the relevant variables before taking a step.

If we want to do more advanced things with the previous state(s) later, that's definitely something we could do, but I think all the memory allocations in one of the innermost loops is probably not worth it compared to copying the values we need out.

This change could probably use a more real benchmark to justify it, but I wanted to get it posted for feedback before I invest much more time into it.

+229 -239

11 comments

3 changed files

bocklund

pr closed time in 14 days