Giter VIP home page Giter VIP logo

Comments (7)

ianhbell avatar ianhbell commented on June 24, 2024 1

Perfecto. That hack works.

from pycalphad.

richardotis avatar richardotis commented on June 24, 2024

Good find! I suspect the root cause of the issue is this (taken from model.py, starting at line 255):

self.models = OrderedDict()
self.build_phase(dbe)

for name, value in self.models.items():
    # XXX: xreplace hack because SymEngine seems to let Symbols slip in somehow
    self.models[name] = self.symbol_replace(value, symbols).xreplace(v.supported_variables_in_databases)

Your custom build_phase is getting called after self.models is initialized, but before symbol replacement happens. Your call to self.GM uses self.models under the hood, so effectively you're accessing it before it's fully initialized. When you call .diff(v.T), I suspect self.GM actually contains some nodes with symengine.Symbol('T') instead of pycalphad.variables.T, so the result of the automatic differentiation is incorrect.

(These issues with early evaluation of properties were part of the motivation for developing the upcoming Computable Property Framework ( #432 ), which should help obviate some of these issues. This current approach will continue to work as well, though we will recommend the CPF moving forward.)

Can you try modifying your custom class like this, to see if it works?

from symengine import Symbol

class MyModel(Model):
    def build_phase(self, dbe):
        super(MyModel, self).build_phase(dbe)
        symbols = {Symbol(s): val for s, val in dbf.symbols.items()}
        for name, value in self.models.items():
            # XXX: xreplace hack because SymEngine seems to let Symbols slip in somehow
            self.models[name] = self.symbol_replace(value, symbols).xreplace(v.supported_variables_in_databases)
        self.entropyval = self.build_entropy(dbe)

    def build_entropy(self, _):
        Gibbs_energy = self.GM
        entropy = -Gibbs_energy.diff(v.T)
        return entropy

The fix for this specific issue should be to move that symbol replacement code into Model.build_phase, so that when you call the build_phase parent class, symbol replacement will occur before control is returned to your custom method. Interested in contributing a PR?

from pycalphad.

ianhbell avatar ianhbell commented on June 24, 2024

Thanks for the super fast response. Alas I must not have a new enough version since this import fails: from pycalphad.io.tdb import get_supported_variables. Do I need to build/install from source?

from pycalphad.

richardotis avatar richardotis commented on June 24, 2024

Sorry about that, I was looking at a development branch. Try the edited version.

from pycalphad.

ianhbell avatar ianhbell commented on June 24, 2024

For future readers, the fixed code:

from tinydb import where
import pycalphad
from pycalphad import Model, variables as v
from pycalphad import Database
from pycalphad import calculate
from symengine import Symbol

import pandas
import numpy as np
print('Version:', pycalphad.__version__)

with open('unary50edt.tdb') as fp:
    dbf = Database(fp.read().upper())

class MyModel(Model):
    def build_phase(self, dbe):
        super(MyModel, self).build_phase(dbe)
        symbols = {Symbol(s): val for s, val in dbf.symbols.items()}
        for name, value in self.models.items():
            # XXX: xreplace hack because SymEngine seems to let Symbols slip in somehow
            self.models[name] = self.symbol_replace(value, symbols).xreplace(v.supported_variables_in_databases)
        
        self.entropyval = self.build_entropy(dbe)

    def build_entropy(self, _):
        Gibbs_energy = self.GM
        entropy = -Gibbs_energy.diff(v.T)
        return entropy

for atom in ['Ni']:
    mod = MyModel(dbf, [atom], 'LIQUID')

    # NOTICE: we need to tell pycalphad about our model for this phase
    models = {'LIQUID': mod}

    o = []
    for temp in np.arange(950, 2000, 200):
        res = calculate(dbf, [atom], 'LIQUID', P=101325, T=temp, model=models, output='SM')
        sA = res.SM.values.squeeze()
        res = calculate(dbf, [atom], 'LIQUID', P=101325, T=temp, model=models, output='entropyval')
        sB = res.entropyval.values.squeeze()
        print(temp, sA, sB)
        
    pandas.DataFrame(o).to_csv(f'{atom}_CALPHAD_test.csv', index=False)

yields

Version: 0.10.2.dev14+g8d16d69
950 74.3563867319028 74.3563867319028
1150 80.55645325671628 80.55645325671628
1350 86.13576559192114 86.13576559192114
1550 91.33373041483597 91.33373041483597
1750 96.34571598414678 96.34571598414678
1950 101.00972148214086 101.00972148214086

from pycalphad.

ianhbell avatar ianhbell commented on June 24, 2024

Is the PR simply a copy-paste into the base class in build_phase? I could try a PR, but I don't know anything about the guts.

from pycalphad.

richardotis avatar richardotis commented on June 24, 2024

Yes, it's close to a copy-paste. You'd want to change the build_phase method in Model to something like:

    def build_phase(self, dbe):
        """
        Generate the symbolic form of all the contributions to this phase.
        Parameters
        ----------
        dbe : Database
        """
        contrib_vals = list(OrderedDict(self.__class__.contributions).values())
        if 'atomic_ordering_energy' in contrib_vals:
            if contrib_vals.index('atomic_ordering_energy') != (len(contrib_vals) - 1):
                # Check for a common mistake in custom models
                # Users that need to override this behavior should override build_phase
                raise ValueError('\'atomic_ordering_energy\' must be the final contribution')
        self.models.clear()
        for key, value in self.__class__.contributions:
            self.models[key] = S(getattr(self, value)(dbe))

        symbols = {Symbol(s): val for s, val in dbe.symbols.items()}

        for name, value in self.models.items():
            # XXX: xreplace hack because SymEngine seems to let Symbols slip in somehow
            self.models[name] = self.symbol_replace(value, symbols).xreplace(v.supported_variables_in_databases)

And then removing that same symbol replacement loop in the __init__ method.

from pycalphad.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.