From 9eed7c962f79cc7a41ac875391db5ab246d57c01 Mon Sep 17 00:00:00 2001 From: Richard Otis Date: Thu, 13 Oct 2022 09:13:06 -0700 Subject: [PATCH 01/31] FIX: Model: Optimize away trivial Piecewise branches for performance --- pycalphad/model.py | 15 + .../tests/databases/FeNi_deep_branching.tdb | 380 ++++++++++++++++++ pycalphad/tests/test_model.py | 11 + 3 files changed, 406 insertions(+) create mode 100644 pycalphad/tests/databases/FeNi_deep_branching.tdb diff --git a/pycalphad/model.py b/pycalphad/model.py index 617cf3a6d..175c3724b 100644 --- a/pycalphad/model.py +++ b/pycalphad/model.py @@ -258,6 +258,21 @@ def __init__(self, dbe, comps, phase_name, parameters=None): # 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) + def unwrap_piecewise(graph): + replace_dict = {} + for atom in graph.atoms(Piecewise): + args = atom.args + # Unwrap temperature-dependent piecewise with zero-defaults + if len(args) == 4 and args[2] == 0 and args[3] == True and atom.args[1].free_symbols == {v.T}: + replace_dict[atom] = args[0] + elif len(args) == 4 and args[0] == 0 and args[2] == 0: + replace_dict[atom] = 0 + return graph.xreplace(replace_dict) + + for name, value in self.models.items(): + for _ in range(5): + self.models[name] = unwrap_piecewise(self.models[name]) + self.site_fractions = sorted([x for x in self.variables if isinstance(x, v.SiteFraction)], key=str) self.state_variables = sorted([x for x in self.variables if not isinstance(x, v.SiteFraction)], key=str) diff --git a/pycalphad/tests/databases/FeNi_deep_branching.tdb b/pycalphad/tests/databases/FeNi_deep_branching.tdb new file mode 100644 index 000000000..e52b812bf --- /dev/null +++ b/pycalphad/tests/databases/FeNi_deep_branching.tdb @@ -0,0 +1,380 @@ +$ NOT FOR RESEARCH -- TESTING ONLY $ +$ +ELEMENT FE BCC_A2 55.847 4489.0 27.28 ! +ELEMENT NI FCC_A1 58.69 4787.0 29.796 ! +ELEMENT VA VACUUM 0.0 0.0 0.0 ! + + +FUNCTION ABF0AAA 298.15 -0.75*DGF0A3B# + 0.75*DGF1A3B#; 6000.0 N ! +FUNCTION ABF0AAB 298.15 -0.25*DGF0A3B# - 0.5*DGF0AB# - 0.25*DGF1A3B# + + 0.5*DGF1AB#; 6000.0 N ! +FUNCTION ABF0ABB 298.15 -0.5*DGF0AB# - 0.25*DGF0B3A# - 0.5*DGF1AB# + + 0.25*DGF1B3A#; 6000.0 N ! +FUNCTION ABF0BBB 298.15 -0.75*DGF0B3A# - 0.75*DGF1B3A#; 6000.0 N ! +FUNCTION ABF1AAA 298.15 -0.75*DGF1A3B#; 6000.0 N ! +FUNCTION ABF1AAB 298.15 -0.25*DGF1A3B# - 0.5*DGF1AB#; 6000.0 N ! +FUNCTION ABF1ABB 298.15 -0.5*DGF1AB# - 0.25*DGF1B3A#; 6000.0 N ! +FUNCTION ABF1BBB 298.15 -0.75*DGF1B3A#; 6000.0 N ! +FUNCTION BF0LAAA 298.15 -0.75*DBF0A3B# + 0.75*DBF1A3B#; 6000.0 N ! +FUNCTION BF0LAAB 298.15 -0.25*DBF0A3B# - 0.5*DBF0AB# - 0.25*DBF1A3B# + + 0.5*DBF1AB#; 6000.0 N ! +FUNCTION BF0LABB 298.15 -0.5*DBF0AB# - 0.25*DBF0B3A# - 0.5*DBF1AB# + + 0.25*DBF1B3A#; 6000.0 N ! +FUNCTION BF0LBBB 298.15 -0.75*DBF0B3A# - 0.75*DBF1B3A#; 6000.0 N ! +FUNCTION BF1LAAA 298.15 -0.75*DBF1A3B#; 6000.0 N ! +FUNCTION BF1LAAB 298.15 -0.75*DBF1A3B#; 6000.0 N ! +FUNCTION BF1LABB 298.15 -0.5*DBF1AB# - 0.25*DBF1B3A#; 6000.0 N ! +FUNCTION BF1LBBB 298.15 -0.75*DBF1B3A#; 6000.0 N ! +FUNCTION BLFAB0 298.15 5; 6000.0 N ! +FUNCTION BLFAB1 298.15 3; 6000.0 N ! +FUNCTION BLFAB2 298.15 2.006; 6000.0 N ! +FUNCTION BLFAB3 298.15 -180.828; 6000.0 N ! +FUNCTION BLFAB4 298.15 12.686; 6000.0 N ! +FUNCTION BMAFCC 298.15 -7.1; 6000.0 N ! +FUNCTION BMBFCC 298.15 2.62; 6000.0 N ! +FUNCTION DBF0A3B 298.15 2.115; 6000.0 N ! +FUNCTION DBF0AB 298.15 7.115; 6000.0 N ! +FUNCTION DBF0B3A 298.15 20.115; 6000.0 N ! +FUNCTION DBF1A3B 298.15 0.0; 6000.0 N ! +FUNCTION DBF1AB 298.15 0.0; 6000.0 N ! +FUNCTION DBF1B3A 298.15 0.0; 6000.0 N ! +FUNCTION DGF0A3B 298.15 44470.65465; 6000.0 N ! +FUNCTION DGF0AB 298.15 1320; 6000.0 N ! +FUNCTION DGF0B3A 298.15 -3001.01879; 6000.0 N ! +FUNCTION DGF1A3B 298.15 0.0; 6000.0 N ! +FUNCTION DGF1AB 298.15 0.0; 6000.0 N ! +FUNCTION DGF1B3A 298.15 166.93282; 6000.0 N ! + +FUNCTION DTF0A3B 298.15 945; 6000.0 N ! +FUNCTION DTF0AB 298.15 122; 6000.0 N ! +FUNCTION DTF0B3A 298.15 879; 6000.0 N ! +FUNCTION DTF1A3B 298.15 0.0; 6000.0 N ! +FUNCTION DTF1AB 298.15 0.0; 6000.0 N ! +FUNCTION DTF1B3A 298.15 0.0; 6000.0 N ! + +FUNCTION TF0LBBB 298.15 -0.75*DTF0B3A# - 0.75*DTF1B3A#; 6000.0 N ! +FUNCTION TF1LAAA 298.15 -0.75*DTF1A3B#; 6000.0 N ! +FUNCTION TF1LAAB 298.15 -0.25*DTF1A3B# - 0.5*DTF1AB#; 6000.0 N ! +FUNCTION TF1LABB 298.15 -0.5*DTF1AB# - 0.25*DTF1B3A#; 6000.0 N ! +FUNCTION TF1LBBB 298.15 -0.75*DTF1B3A#; 6000.0 N ! +FUNCTION TLFAB0 298.15 1295.6; 6000.0 N ! +FUNCTION TLFAB1 298.15 -248.8; 6000.0 N ! +FUNCTION TLFAB2 298.15 -1086.0; 6000.0 N ! +FUNCTION TLFAB3 298.15 -2367.6; 6000.0 N ! +FUNCTION TLFAB4 298.15 -3642.1; 6000.0 N ! +FUNCTION TLFAB5 298.15 -7424.0; 6000.0 N ! +FUNCTION GLFAB0 298.15 1.2333*T - 15690.2618; 6000.0 N ! +FUNCTION GLFAB1 298.15 7781.85057; 6000.0 N ! +FUNCTION GLFAB2 298.15 -1649.56071; 6000.0 N ! +FUNCTION TCAFCC 298.15 -201; 6000.0 N ! +FUNCTION TCBFCC 298.15 625; 6000.0 N ! +FUNCTION TF0LAAA 298.15 -0.75*DTF0A3B# + 0.75*DTF1A3B#; 6000.0 N ! +FUNCTION TF0LAAB 298.15 -0.25*DTF0A3B# - 0.5*DTF0AB# - 0.25*DTF1A3B# + + 0.5*DTF1AB#; 6000.0 N ! +FUNCTION TF0LABB 298.15 -0.5*DTF0AB# - 0.25*DTF0B3A# - 0.5*DTF1AB# + + 0.25*DTF1B3A#; 6000.0 N ! + +FUNCTION GFCCFE 298.15 0.00064*T**2 - 1.15*T*LN(T) + 8.282*T + GHSERFE# - + 1462.4; 1811.0 Y 0.94001*T + GHSERFE# - 1713.815 + 4.9251E+30*T**(-9); + 6000.0 N ! + +FUNCTION GHSERFE 298.15 -5.8927E-8*T**3 - 0.00439752*T**2 - 23.5143*T*LN(T) + + 124.134*T + 1225.7 + 77359*T**(-1); 1811.0 Y -46*T*LN(T) + 299.31255*T - + 25383.581 + 2.29603E+31*T**(-9); 6000.0 N ! + +FUNCTION GHSERNI 273.0 -0.0048407*T**2 - 22.096*T*LN(T) + 117.854*T - + 5179.159; 1728.0 Y -43.1*T*LN(T) + 279.135*T - 27840.655 + + 1.12754E+31*T**(-9); 6000.0 N ! + + +TYPE_DEFINITION % SEQ * ! +DEFINE_SYSTEM_DEFAULT ELEMENT 2 ! +DEFAULT_COMMAND DEFINE_SYSTEM_ELEMENT VA ! + +TYPE_DEFINITION * GES AMEND_PHASE_DESCRIPTION FCC_A1 MAGNETIC -3.0 0.28 ! +TYPE_DEFINITION ' GES AMEND_PHASE_DESCRIPTION ORD_FCC DISORDERED_PART FCC_A1 ! +TYPE_DEFINITION A GES AMEND_PHASE_DESCRIPTION ORD_FCC MAGNETIC -3.0 0.28 ! + +PHASE FCC_A1 %* 2 1.0 1.0 ! +CONSTITUENT FCC_A1 :FE,NI:VA: ! + +PHASE ORD_FCC %'A 5 0.25 0.25 0.25 0.25 1.0 ! +CONSTITUENT ORD_FCC :FE,NI:FE,NI +:FE,NI:FE,NI:VA: ! + + +$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ +$ FE $ +$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ + +PARAMETER BMAGN(FCC_A1,FE:VA;0) 298.15 -2.1; 6000.0 N ! +PARAMETER G(FCC_A1,FE:VA;0) 273.0 GFCCFE; 6000.0 N ! +PARAMETER NT(FCC_A1,FE:VA;0) 298.15 201; 6000.0 N ! +PARAMETER TC(FCC_A1,FE:VA;0) 298.15 -201; 6000.0 N ! + + +$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ +$ NI $ +$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ + +PARAMETER BMAGN(FCC_A1,NI:VA;0) 298.15 0.52; 6000.0 N ! +PARAMETER G(FCC_A1,NI:VA;0) 298.15 GHSERNI#; 6000.0 N ! +PARAMETER TC(FCC_A1,NI:VA;0) 298.15 633; 6000.0 N ! + + + + + +$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ +$ FE-NI $ +$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ + +PARAMETER BMAGN(FCC_A1,FE,NI:VA;0) 298.15 BLFAB0#; 6000.0 N ! +PARAMETER BMAGN(FCC_A1,FE,NI:VA;1) 298.15 BLFAB1#; 6000.0 N ! +PARAMETER BMAGN(FCC_A1,FE,NI:VA;2) 298.15 BLFAB2#; 6000.0 N ! +PARAMETER BMAGN(FCC_A1,FE,NI:VA;3) 298.15 BLFAB3#; 6000.0 N ! +PARAMETER BMAGN(FCC_A1,FE,NI:VA;4) 298.15 BLFAB4#; 6000.0 N ! +PARAMETER G(FCC_A1,FE,NI:VA;0) 298.15 GLFAB0#; 6000.0 N ! +PARAMETER G(FCC_A1,FE,NI:VA;1) 298.15 GLFAB1#; 6000.0 N ! +PARAMETER G(FCC_A1,FE,NI:VA;2) 298.15 GLFAB2#; 6000.0 N ! +PARAMETER TC(FCC_A1,FE,NI:VA;0) 298.15 TLFAB0#; 6000.0 N ! +PARAMETER TC(FCC_A1,FE,NI:VA;1) 298.15 TLFAB1#; 6000.0 N ! +PARAMETER TC(FCC_A1,FE,NI:VA;2) 298.15 TLFAB2#; 6000.0 N ! +PARAMETER TC(FCC_A1,FE,NI:VA;3) 298.15 TLFAB3#; 6000.0 N ! +PARAMETER TC(FCC_A1,FE,NI:VA;4) 298.15 TLFAB4#; 6000.0 N ! +PARAMETER TC(FCC_A1,FE,NI:VA;5) 298.15 TLFAB5#; 6000.0 N ! + +PARAMETER BMAGN(ORD_FCC,FE:FE:FE:NI:VA;0) 298.15 0.75*DBF0A3B#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE:FE:NI:FE:VA;0) 298.15 0.75*DBF0A3B#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE:FE:NI:NI:VA;0) 298.15 DBF0AB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE:NI:FE:FE:VA;0) 298.15 0.75*DBF0A3B#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE:NI:FE:NI:VA;0) 298.15 DBF0AB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE:NI:NI:FE:VA;0) 298.15 DBF0AB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE:NI:NI:NI:VA;0) 298.15 0.75*DBF0B3A#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,NI:FE:FE:FE:VA;0) 298.15 0.75*DBF0A3B#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,NI:FE:FE:NI:VA;0) 298.15 DBF0AB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,NI:FE:NI:FE:VA;0) 298.15 DBF0AB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,NI:FE:NI:NI:VA;0) 298.15 0.75*DBF0B3A#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,NI:NI:FE:FE:VA;0) 298.15 DBF0AB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,NI:NI:FE:NI:VA;0) 298.15 0.75*DBF0B3A#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,NI:NI:NI:FE:VA;0) 298.15 0.75*DBF0B3A#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE:FE:FE:FE,NI:VA;0) 298.15 BF0LAAA#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE:FE:FE:FE,NI:VA;1) 298.15 BF1LAAA#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE:FE:FE,NI:FE:VA;0) 298.15 BF0LAAA#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE:FE:FE,NI:FE:VA;1) 298.15 BF1LAAA#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE:FE:FE,NI:NI:VA;0) 298.15 BF0LAAB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE:FE:FE,NI:NI:VA;1) 298.15 BF1LAAB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE:FE:NI:FE,NI:VA;0) 298.15 BF0LAAB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE:FE:NI:FE,NI:VA;1) 298.15 BF1LAAB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE:FE,NI:FE:FE:VA;0) 298.15 BF0LAAA#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE:FE,NI:FE:FE:VA;1) 298.15 BF1LAAA#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE:FE,NI:FE:NI:VA;0) 298.15 BF0LAAB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE:FE,NI:FE:NI:VA;1) 298.15 BF1LAAB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE:FE,NI:NI:FE:VA;0) 298.15 BF0LAAB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE:FE,NI:NI:FE:VA;1) 298.15 BF1LAAB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE:FE,NI:NI:NI:VA;0) 298.15 BF0LABB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE:FE,NI:NI:NI:VA;1) 298.15 BF1LABB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE:NI:FE:FE,NI:VA;0) 298.15 BF0LAAB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE:NI:FE:FE,NI:VA;1) 298.15 BF1LAAB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE:NI:FE,NI:FE:VA;0) 298.15 BF0LAAB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE:NI:FE,NI:FE:VA;1) 298.15 BF1LAAB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE:NI:FE,NI:NI:VA;0) 298.15 BF0LABB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE:NI:FE,NI:NI:VA;1) 298.15 BF1LABB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE:NI:NI:FE,NI:VA;0) 298.15 BF0LABB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE:NI:NI:FE,NI:VA;1) 298.15 BF1LABB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE,NI:FE:FE:FE:VA;0) 298.15 BF0LAAA#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE,NI:FE:FE:FE:VA;1) 298.15 BF1LAAA#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE,NI:FE:FE:NI:VA;0) 298.15 BF0LAAB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE,NI:FE:FE:NI:VA;1) 298.15 BF1LAAB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE,NI:FE:NI:FE:VA;0) 298.15 BF0LAAB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE,NI:FE:NI:FE:VA;1) 298.15 BF1LAAB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE,NI:FE:NI:NI:VA;0) 298.15 BF0LABB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE,NI:FE:NI:NI:VA;1) 298.15 BF1LABB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE,NI:NI:FE:FE:VA;0) 298.15 BF0LAAB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE,NI:NI:FE:FE:VA;1) 298.15 BF1LAAB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE,NI:NI:FE:NI:VA;0) 298.15 BF0LABB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE,NI:NI:FE:NI:VA;1) 298.15 BF1LABB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE,NI:NI:NI:FE:VA;0) 298.15 BF0LABB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE,NI:NI:NI:FE:VA;1) 298.15 BF1LABB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE,NI:NI:NI:NI:VA;0) 298.15 BF0LBBB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,FE,NI:NI:NI:NI:VA;1) 298.15 BF1LBBB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,NI:FE:FE:FE,NI:VA;0) 298.15 BF0LAAB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,NI:FE:FE:FE,NI:VA;1) 298.15 BF1LAAB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,NI:FE:FE,NI:FE:VA;0) 298.15 BF0LAAB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,NI:FE:FE,NI:FE:VA;1) 298.15 BF1LAAB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,NI:FE:FE,NI:NI:VA;0) 298.15 BF0LABB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,NI:FE:FE,NI:NI:VA;1) 298.15 BF1LABB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,NI:FE:NI:FE,NI:VA;0) 298.15 BF0LABB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,NI:FE:NI:FE,NI:VA;1) 298.15 BF1LABB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,NI:FE,NI:FE:FE:VA;0) 298.15 BF0LAAB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,NI:FE,NI:FE:FE:VA;1) 298.15 BF1LAAB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,NI:FE,NI:FE:NI:VA;0) 298.15 BF0LABB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,NI:FE,NI:FE:NI:VA;1) 298.15 BF1LABB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,NI:FE,NI:NI:FE:VA;0) 298.15 BF0LABB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,NI:FE,NI:NI:FE:VA;1) 298.15 BF1LABB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,NI:FE,NI:NI:NI:VA;0) 298.15 BF0LBBB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,NI:FE,NI:NI:NI:VA;1) 298.15 BF1LBBB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,NI:NI:FE:FE,NI:VA;0) 298.15 BF0LABB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,NI:NI:FE:FE,NI:VA;1) 298.15 BF1LABB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,NI:NI:FE,NI:FE:VA;0) 298.15 BF0LABB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,NI:NI:FE,NI:FE:VA;1) 298.15 BF1LABB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,NI:NI:FE,NI:NI:VA;0) 298.15 BF0LBBB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,NI:NI:FE,NI:NI:VA;1) 298.15 BF1LBBB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,NI:NI:NI:FE,NI:VA;0) 298.15 BF0LBBB#; 6000.0 N ! +PARAMETER BMAGN(ORD_FCC,NI:NI:NI:FE,NI:VA;1) 298.15 BF1LBBB#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE:FE:FE:NI:VA;0) 298.15 0.75*DGF0A3B#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE:FE:NI:FE:VA;0) 298.15 0.75*DGF0A3B#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE:FE:NI:NI:VA;0) 298.15 DGF0AB#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE:NI:FE:FE:VA;0) 298.15 0.75*DGF0A3B#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE:NI:FE:NI:VA;0) 298.15 DGF0AB#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE:NI:NI:FE:VA;0) 298.15 DGF0AB#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE:NI:NI:NI:VA;0) 298.15 0.75*DGF0B3A#; 6000.0 N ! +PARAMETER G(ORD_FCC,NI:FE:FE:FE:VA;0) 298.15 0.75*DGF0A3B#; 6000.0 N ! +PARAMETER G(ORD_FCC,NI:FE:FE:NI:VA;0) 298.15 DGF0AB#; 6000.0 N ! +PARAMETER G(ORD_FCC,NI:FE:NI:FE:VA;0) 298.15 DGF0AB#; 6000.0 N ! +PARAMETER G(ORD_FCC,NI:FE:NI:NI:VA;0) 298.15 0.75*DGF0B3A#; 6000.0 N ! +PARAMETER G(ORD_FCC,NI:NI:FE:FE:VA;0) 298.15 DGF0AB#; 6000.0 N ! +PARAMETER G(ORD_FCC,NI:NI:FE:NI:VA;0) 298.15 0.75*DGF0B3A#; 6000.0 N ! +PARAMETER G(ORD_FCC,NI:NI:NI:FE:VA;0) 298.15 0.75*DGF0B3A#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE:FE:FE:FE,NI:VA;0) 298.15 ABF0AAA#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE:FE:FE:FE,NI:VA;1) 298.15 ABF1AAA#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE:FE:FE,NI:FE:VA;0) 298.15 ABF0AAA#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE:FE:FE,NI:FE:VA;1) 298.15 ABF1AAA#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE:FE:FE,NI:NI:VA;0) 298.15 ABF0AAB#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE:FE:FE,NI:NI:VA;1) 298.15 ABF1AAB#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE:FE:NI:FE,NI:VA;0) 298.15 ABF0AAB#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE:FE:NI:FE,NI:VA;1) 298.15 ABF1AAB#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE:FE,NI:FE:FE:VA;0) 298.15 ABF0AAA#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE:FE,NI:FE:FE:VA;1) 298.15 ABF1AAA#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE:FE,NI:FE:NI:VA;0) 298.15 ABF0AAB#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE:FE,NI:FE:NI:VA;1) 298.15 ABF1AAB#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE:FE,NI:NI:FE:VA;0) 298.15 ABF0AAB#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE:FE,NI:NI:FE:VA;1) 298.15 ABF1AAB#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE:FE,NI:NI:NI:VA;0) 298.15 ABF0ABB#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE:FE,NI:NI:NI:VA;1) 298.15 ABF1ABB#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE:NI:FE:FE,NI:VA;0) 298.15 ABF0AAB#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE:NI:FE:FE,NI:VA;1) 298.15 ABF1AAB#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE:NI:FE,NI:FE:VA;0) 298.15 ABF0AAB#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE:NI:FE,NI:FE:VA;1) 298.15 ABF1AAB#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE:NI:FE,NI:NI:VA;0) 298.15 ABF0ABB#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE:NI:FE,NI:NI:VA;1) 298.15 ABF1ABB#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE:NI:NI:FE,NI:VA;0) 298.15 ABF0ABB#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE:NI:NI:FE,NI:VA;1) 298.15 ABF1ABB#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE,NI:FE:FE:FE:VA;0) 298.15 ABF0AAA#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE,NI:FE:FE:FE:VA;1) 298.15 ABF1AAA#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE,NI:FE:FE:NI:VA;0) 298.15 ABF0AAB#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE,NI:FE:FE:NI:VA;1) 298.15 ABF1AAB#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE,NI:FE:NI:FE:VA;0) 298.15 ABF0AAB#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE,NI:FE:NI:FE:VA;1) 298.15 ABF1AAB#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE,NI:FE:NI:NI:VA;0) 298.15 ABF0ABB#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE,NI:FE:NI:NI:VA;1) 298.15 ABF1ABB#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE,NI:NI:FE:FE:VA;0) 298.15 ABF0AAB#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE,NI:NI:FE:FE:VA;1) 298.15 ABF1AAB#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE,NI:NI:FE:NI:VA;0) 298.15 ABF0ABB#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE,NI:NI:FE:NI:VA;1) 298.15 ABF1ABB#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE,NI:NI:NI:FE:VA;0) 298.15 ABF0ABB#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE,NI:NI:NI:FE:VA;1) 298.15 ABF1ABB#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE,NI:NI:NI:NI:VA;0) 298.15 ABF0BBB#; 6000.0 N ! +PARAMETER G(ORD_FCC,FE,NI:NI:NI:NI:VA;1) 298.15 ABF1BBB#; 6000.0 N ! +PARAMETER G(ORD_FCC,NI:FE:FE:FE,NI:VA;0) 298.15 ABF0AAB#; 6000.0 N ! +PARAMETER G(ORD_FCC,NI:FE:FE:FE,NI:VA;1) 298.15 ABF1AAB#; 6000.0 N ! +PARAMETER G(ORD_FCC,NI:FE:FE,NI:FE:VA;0) 298.15 ABF0AAB#; 6000.0 N ! +PARAMETER G(ORD_FCC,NI:FE:FE,NI:FE:VA;1) 298.15 ABF1AAB#; 6000.0 N ! +PARAMETER G(ORD_FCC,NI:FE:FE,NI:NI:VA;0) 298.15 ABF0ABB#; 6000.0 N ! +PARAMETER G(ORD_FCC,NI:FE:FE,NI:NI:VA;1) 298.15 ABF1ABB#; 6000.0 N ! +PARAMETER G(ORD_FCC,NI:FE:NI:FE,NI:VA;0) 298.15 ABF0ABB#; 6000.0 N ! +PARAMETER G(ORD_FCC,NI:FE:NI:FE,NI:VA;1) 298.15 ABF1ABB#; 6000.0 N ! +PARAMETER G(ORD_FCC,NI:FE,NI:FE:FE:VA;0) 298.15 ABF0AAB#; 6000.0 N ! +PARAMETER G(ORD_FCC,NI:FE,NI:FE:FE:VA;1) 298.15 ABF1AAB#; 6000.0 N ! +PARAMETER G(ORD_FCC,NI:FE,NI:FE:NI:VA;0) 298.15 ABF0ABB#; 6000.0 N ! +PARAMETER G(ORD_FCC,NI:FE,NI:FE:NI:VA;1) 298.15 ABF1ABB#; 6000.0 N ! +PARAMETER G(ORD_FCC,NI:FE,NI:NI:FE:VA;0) 298.15 ABF0ABB#; 6000.0 N ! +PARAMETER G(ORD_FCC,NI:FE,NI:NI:FE:VA;1) 298.15 ABF1ABB#; 6000.0 N ! +PARAMETER G(ORD_FCC,NI:FE,NI:NI:NI:VA;0) 298.15 ABF0BBB#; 6000.0 N ! +PARAMETER G(ORD_FCC,NI:FE,NI:NI:NI:VA;1) 298.15 ABF1BBB#; 6000.0 N ! +PARAMETER G(ORD_FCC,NI:NI:FE:FE,NI:VA;0) 298.15 ABF0ABB#; 6000.0 N ! +PARAMETER G(ORD_FCC,NI:NI:FE:FE,NI:VA;1) 298.15 ABF1ABB#; 6000.0 N ! +PARAMETER G(ORD_FCC,NI:NI:FE,NI:FE:VA;0) 298.15 ABF0ABB#; 6000.0 N ! +PARAMETER G(ORD_FCC,NI:NI:FE,NI:FE:VA;1) 298.15 ABF1ABB#; 6000.0 N ! +PARAMETER G(ORD_FCC,NI:NI:FE,NI:NI:VA;0) 298.15 ABF0BBB#; 6000.0 N ! +PARAMETER G(ORD_FCC,NI:NI:FE,NI:NI:VA;1) 298.15 ABF1BBB#; 6000.0 N ! +PARAMETER G(ORD_FCC,NI:NI:NI:FE,NI:VA;0) 298.15 ABF0BBB#; 6000.0 N ! +PARAMETER G(ORD_FCC,NI:NI:NI:FE,NI:VA;1) 298.15 ABF1BBB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE:FE:FE:NI:VA;0) 298.15 0.75*DTF0A3B#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE:FE:NI:FE:VA;0) 298.15 0.75*DTF0A3B#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE:FE:NI:NI:VA;0) 298.15 DTF0AB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE:NI:FE:FE:VA;0) 298.15 0.75*DTF0A3B#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE:NI:FE:NI:VA;0) 298.15 DTF0AB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE:NI:NI:FE:VA;0) 298.15 DTF0AB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE:NI:NI:NI:VA;0) 298.15 0.75*DTF0B3A#; 6000.0 N ! +PARAMETER TC(ORD_FCC,NI:FE:FE:FE:VA;0) 298.15 0.75*DTF0A3B#; 6000.0 N ! +PARAMETER TC(ORD_FCC,NI:FE:FE:NI:VA;0) 298.15 DTF0AB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,NI:FE:NI:FE:VA;0) 298.15 DTF0AB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,NI:FE:NI:NI:VA;0) 298.15 0.75*DTF0B3A#; 6000.0 N ! +PARAMETER TC(ORD_FCC,NI:NI:FE:FE:VA;0) 298.15 DTF0AB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,NI:NI:FE:NI:VA;0) 298.15 0.75*DTF0B3A#; 6000.0 N ! +PARAMETER TC(ORD_FCC,NI:NI:NI:FE:VA;0) 298.15 0.75*DTF0B3A#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE:FE:FE:FE,NI:VA;0) 298.15 TF0LAAA#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE:FE:FE:FE,NI:VA;1) 298.15 TF1LAAA#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE:FE:FE,NI:FE:VA;0) 298.15 TF0LAAA#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE:FE:FE,NI:FE:VA;1) 298.15 TF1LAAA#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE:FE:FE,NI:NI:VA;0) 298.15 TF0LAAB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE:FE:FE,NI:NI:VA;1) 298.15 TF1LAAB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE:FE:NI:FE,NI:VA;0) 298.15 TF0LAAB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE:FE:NI:FE,NI:VA;1) 298.15 TF1LAAB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE:FE,NI:FE:FE:VA;0) 298.15 TF0LAAA#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE:FE,NI:FE:FE:VA;1) 298.15 TF1LAAA#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE:FE,NI:FE:NI:VA;0) 298.15 TF0LAAB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE:FE,NI:FE:NI:VA;1) 298.15 TF1LAAB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE:FE,NI:NI:FE:VA;0) 298.15 TF0LAAB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE:FE,NI:NI:FE:VA;1) 298.15 TF1LAAB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE:FE,NI:NI:NI:VA;0) 298.15 TF0LABB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE:FE,NI:NI:NI:VA;1) 298.15 TF1LABB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE:NI:FE:FE,NI:VA;0) 298.15 TF0LAAB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE:NI:FE:FE,NI:VA;1) 298.15 TF1LAAB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE:NI:FE,NI:FE:VA;0) 298.15 TF0LAAB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE:NI:FE,NI:FE:VA;1) 298.15 TF1LAAB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE:NI:FE,NI:NI:VA;0) 298.15 TF0LABB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE:NI:FE,NI:NI:VA;1) 298.15 TF1LABB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE:NI:NI:FE,NI:VA;0) 298.15 TF0LABB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE:NI:NI:FE,NI:VA;1) 298.15 TF1LABB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE,NI:FE:FE:FE:VA;0) 298.15 TF0LAAA#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE,NI:FE:FE:FE:VA;1) 298.15 TF1LAAA#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE,NI:FE:FE:NI:VA;0) 298.15 TF0LAAB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE,NI:FE:FE:NI:VA;1) 298.15 TF1LAAB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE,NI:FE:NI:FE:VA;0) 298.15 TF0LAAB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE,NI:FE:NI:FE:VA;1) 298.15 TF1LAAB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE,NI:FE:NI:NI:VA;0) 298.15 TF0LABB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE,NI:FE:NI:NI:VA;1) 298.15 TF1LABB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE,NI:NI:FE:FE:VA;0) 298.15 TF0LAAB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE,NI:NI:FE:FE:VA;1) 298.15 TF1LAAB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE,NI:NI:FE:NI:VA;0) 298.15 TF0LABB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE,NI:NI:FE:NI:VA;1) 298.15 TF1LABB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE,NI:NI:NI:FE:VA;0) 298.15 TF0LABB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE,NI:NI:NI:FE:VA;1) 298.15 TF1LABB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE,NI:NI:NI:NI:VA;0) 298.15 TF0LBBB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,FE,NI:NI:NI:NI:VA;1) 298.15 TF1LBBB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,NI:FE:FE:FE,NI:VA;0) 298.15 TF0LAAB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,NI:FE:FE:FE,NI:VA;1) 298.15 TF1LAAB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,NI:FE:FE,NI:FE:VA;0) 298.15 TF0LAAB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,NI:FE:FE,NI:FE:VA;1) 298.15 TF1LAAB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,NI:FE:FE,NI:NI:VA;0) 298.15 TF0LABB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,NI:FE:FE,NI:NI:VA;1) 298.15 TF1LABB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,NI:FE:NI:FE,NI:VA;0) 298.15 TF0LABB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,NI:FE:NI:FE,NI:VA;1) 298.15 TF1LABB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,NI:FE,NI:FE:FE:VA;0) 298.15 TF0LAAB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,NI:FE,NI:FE:FE:VA;1) 298.15 TF1LAAB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,NI:FE,NI:FE:NI:VA;0) 298.15 TF0LABB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,NI:FE,NI:FE:NI:VA;1) 298.15 TF1LABB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,NI:FE,NI:NI:FE:VA;0) 298.15 TF0LABB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,NI:FE,NI:NI:FE:VA;1) 298.15 TF1LABB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,NI:FE,NI:NI:NI:VA;0) 298.15 TF0LBBB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,NI:FE,NI:NI:NI:VA;1) 298.15 TF1LBBB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,NI:NI:FE:FE,NI:VA;0) 298.15 TF0LABB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,NI:NI:FE:FE,NI:VA;1) 298.15 TF1LABB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,NI:NI:FE,NI:FE:VA;0) 298.15 TF0LABB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,NI:NI:FE,NI:FE:VA;1) 298.15 TF1LABB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,NI:NI:FE,NI:NI:VA;0) 298.15 TF0LBBB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,NI:NI:FE,NI:NI:VA;1) 298.15 TF1LBBB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,NI:NI:NI:FE,NI:VA;0) 298.15 TF0LBBB#; 6000.0 N ! +PARAMETER TC(ORD_FCC,NI:NI:NI:FE,NI:VA;1) 298.15 TF1LBBB#; 6000.0 N ! diff --git a/pycalphad/tests/test_model.py b/pycalphad/tests/test_model.py index 7a0277c15..111984df2 100644 --- a/pycalphad/tests/test_model.py +++ b/pycalphad/tests/test_model.py @@ -4,6 +4,7 @@ from pycalphad import Database, Model, variables as v, equilibrium from pycalphad.tests.fixtures import select_database, load_database from pycalphad.core.errors import DofError +from symengine import Piecewise import numpy as np import pickle import pytest @@ -162,3 +163,13 @@ def test_order_disorder_interstital_sublattice_validation(): # as the substitutional and cannot be distinguished with pytest.raises(ValueError): Model(DBF_OrderDisorder_broken, ["A", "B", "VA"], "ORD_SUBS_INSTL") + +@select_database("FeNi_deep_branching.tdb") +def test_model_deep_branching(load_database): + "Models with very deep piecewise branching are optimized at construction time" + dbf = load_database() + mod = Model(dbf, ['FE', 'NI', 'VA'], 'ORD_FCC') + # All we really care about is that the energy Hessian will calculate without hanging + # However, that is a relatively long test. This just checks that the deep branches were cleaned up. + # Without optimization/unwrapping, this would be about 57 + assert len(mod.GM.atoms(Piecewise)) < 30 \ No newline at end of file From 4c0318f8c06e4cddbb5612afc89dbb20b7b71740 Mon Sep 17 00:00:00 2001 From: Richard Otis Date: Thu, 13 Oct 2022 09:29:49 -0700 Subject: [PATCH 02/31] FIX: Model: Use raw string to avoid escape sequence in docstring --- pycalphad/model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pycalphad/model.py b/pycalphad/model.py index 175c3724b..ac3c5d520 100644 --- a/pycalphad/model.py +++ b/pycalphad/model.py @@ -1132,7 +1132,7 @@ def _partitioned_expr(disord_expr, ord_expr, disordered_mole_fraction_dict, orde return disord_expr + ordering_expr def atomic_ordering_energy(self, dbe): - """ + r""" Return the atomic ordering contribution in symbolic form. If the current phase is anything other than the ordered phase in a From e4698a4b7526f80c34c9ca31868c570cf20e41db Mon Sep 17 00:00:00 2001 From: Richard Otis Date: Thu, 13 Oct 2022 09:34:16 -0700 Subject: [PATCH 03/31] TST: test_model: Add newline at end of file --- pycalphad/tests/test_model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pycalphad/tests/test_model.py b/pycalphad/tests/test_model.py index 111984df2..dec4adcf1 100644 --- a/pycalphad/tests/test_model.py +++ b/pycalphad/tests/test_model.py @@ -172,4 +172,4 @@ def test_model_deep_branching(load_database): # All we really care about is that the energy Hessian will calculate without hanging # However, that is a relatively long test. This just checks that the deep branches were cleaned up. # Without optimization/unwrapping, this would be about 57 - assert len(mod.GM.atoms(Piecewise)) < 30 \ No newline at end of file + assert len(mod.GM.atoms(Piecewise)) < 30 From ec4bc107d7e5410f4d21bc2808daa7088fd23336 Mon Sep 17 00:00:00 2001 From: Richard Otis Date: Thu, 13 Oct 2022 10:48:39 -0700 Subject: [PATCH 04/31] WIP: Model: Try removing unnecessary branch to fix Mac errors --- pycalphad/model.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pycalphad/model.py b/pycalphad/model.py index ac3c5d520..f4c666bf5 100644 --- a/pycalphad/model.py +++ b/pycalphad/model.py @@ -263,10 +263,8 @@ def unwrap_piecewise(graph): for atom in graph.atoms(Piecewise): args = atom.args # Unwrap temperature-dependent piecewise with zero-defaults - if len(args) == 4 and args[2] == 0 and args[3] == True and atom.args[1].free_symbols == {v.T}: + if len(args) == 4 and args[2] == 0 and args[3] == True and args[1].free_symbols == {v.T}: replace_dict[atom] = args[0] - elif len(args) == 4 and args[0] == 0 and args[2] == 0: - replace_dict[atom] = 0 return graph.xreplace(replace_dict) for name, value in self.models.items(): From 6ef8a4cd3cf4c17426c0b75e02b807e2c22b312f Mon Sep 17 00:00:00 2001 From: Richard Otis Date: Thu, 13 Oct 2022 11:03:28 -0700 Subject: [PATCH 05/31] CI test only, should be reverted --- pycalphad/model.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pycalphad/model.py b/pycalphad/model.py index f4c666bf5..a90ca5593 100644 --- a/pycalphad/model.py +++ b/pycalphad/model.py @@ -267,9 +267,9 @@ def unwrap_piecewise(graph): replace_dict[atom] = args[0] return graph.xreplace(replace_dict) - for name, value in self.models.items(): - for _ in range(5): - self.models[name] = unwrap_piecewise(self.models[name]) + #for name, value in self.models.items(): + # for _ in range(5): + # self.models[name] = unwrap_piecewise(self.models[name]) self.site_fractions = sorted([x for x in self.variables if isinstance(x, v.SiteFraction)], key=str) self.state_variables = sorted([x for x in self.variables if not isinstance(x, v.SiteFraction)], key=str) From 671e006924ddf47a2877655c1b668b6060fb7091 Mon Sep 17 00:00:00 2001 From: Richard Otis Date: Thu, 13 Oct 2022 11:50:41 -0700 Subject: [PATCH 06/31] Revert "CI test only, should be reverted" This reverts commit 6ef8a4cd3cf4c17426c0b75e02b807e2c22b312f. --- pycalphad/model.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pycalphad/model.py b/pycalphad/model.py index a90ca5593..f4c666bf5 100644 --- a/pycalphad/model.py +++ b/pycalphad/model.py @@ -267,9 +267,9 @@ def unwrap_piecewise(graph): replace_dict[atom] = args[0] return graph.xreplace(replace_dict) - #for name, value in self.models.items(): - # for _ in range(5): - # self.models[name] = unwrap_piecewise(self.models[name]) + for name, value in self.models.items(): + for _ in range(5): + self.models[name] = unwrap_piecewise(self.models[name]) self.site_fractions = sorted([x for x in self.variables if isinstance(x, v.SiteFraction)], key=str) self.state_variables = sorted([x for x in self.variables if not isinstance(x, v.SiteFraction)], key=str) From 042af2a779f91fb62f82090ec11e4b790daeda0a Mon Sep 17 00:00:00 2001 From: Richard Otis Date: Fri, 14 Oct 2022 11:19:26 -0700 Subject: [PATCH 07/31] FIX/TST: calculate: Do not sample between endmembers when there are too many --- pycalphad/core/calculate.py | 9 ++++++--- pycalphad/tests/test_calculate.py | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/pycalphad/core/calculate.py b/pycalphad/core/calculate.py index 272cdcf4f..a61215410 100644 --- a/pycalphad/core/calculate.py +++ b/pycalphad/core/calculate.py @@ -152,9 +152,12 @@ def _sample_phase_constitution(model, sampler, fixed_grid, pdens): # These constitution space edges are often the equilibrium points! em_pairs = list(itertools.combinations(points, 2)) lingrid = np.linspace(0, 1, pdens) - extra_points = [first_em * lingrid[np.newaxis].T + - second_em * lingrid[::-1][np.newaxis].T - for first_em, second_em in em_pairs] + if len(em_pairs) * pdens < 100000: + extra_points = [first_em * lingrid[np.newaxis].T + + second_em * lingrid[::-1][np.newaxis].T + for first_em, second_em in em_pairs] + else: + extra_points = [] points = np.concatenate(list(itertools.chain([points], extra_points))) # Sample composition space for more points if sum(sublattice_dof) > len(sublattice_dof): diff --git a/pycalphad/tests/test_calculate.py b/pycalphad/tests/test_calculate.py index ca8f76b1d..1b6092718 100644 --- a/pycalphad/tests/test_calculate.py +++ b/pycalphad/tests/test_calculate.py @@ -252,3 +252,21 @@ def test_BCC_phase_with_symmetry_option_B(load_database): bcc_4sl_calc_res = calculate(dbf, ["AL", "FE", "VA"], "BCC_4SL", T=300, N=1, P=101325, pdens=10) bcc_no_B_calc_res = calculate(dbf, ["AL", "FE", "VA"], "BCC_NOB", T=300, N=1, P=101325, pdens=10) assert np.allclose(bcc_4sl_calc_res.GM.values.squeeze(), bcc_no_B_calc_res.GM.values.squeeze()) + +def test_complex_multisublattice(): + "calculate returns a result without running out of memory for a complex multi-sublattice phase" + tdb = """ +ELEMENT CR BCC_A2 51.996 4050.0 23.543 ! +ELEMENT FE BCC_A2 55.847 4489.0 27.28 ! +ELEMENT MO BCC_A2 95.94 4589.0 28.56 ! +ELEMENT NB BCC_A2 92.906 5220.0 36.27 ! +ELEMENT NI FCC_A1 58.69 4787.0 29.796 ! + +PHASE MU_PHASE % 5 2.0 2.0 2.0 6.0 1.0 ! +CONSTITUENT MU_PHASE + :CR,FE,MO,NB,NI:CR,FE,MO,NB,NI:CR,FE,MO,NB,NI: + CR,FE,MO,NB,NI:CR,FE,MO,NB,NI: ! + """ + dbf = Database(tdb) + calc_res = calculate(dbf, ['CR', 'FE', 'MO', 'NB', 'NI'], ['MU_PHASE'], N=1, P=101325, T=300, pdens=60) + assert calc_res.GM.size < 100000 \ No newline at end of file From 5c731ebab2f6cf84d1fced842a8f1773253cda00 Mon Sep 17 00:00:00 2001 From: Richard Otis Date: Fri, 14 Oct 2022 11:37:49 -0700 Subject: [PATCH 08/31] Debugging code --- pycalphad/model.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pycalphad/model.py b/pycalphad/model.py index f4c666bf5..0ae0c1657 100644 --- a/pycalphad/model.py +++ b/pycalphad/model.py @@ -268,6 +268,7 @@ def unwrap_piecewise(graph): return graph.xreplace(replace_dict) for name, value in self.models.items(): + print(name, self.models[name].GM.atoms(Piecewise)) for _ in range(5): self.models[name] = unwrap_piecewise(self.models[name]) From 3ff08a0a837ce8e3dbec5cd1336aeeb4dbe8d9ff Mon Sep 17 00:00:00 2001 From: Richard Otis Date: Fri, 14 Oct 2022 11:42:33 -0700 Subject: [PATCH 09/31] fix typo --- pycalphad/model.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pycalphad/model.py b/pycalphad/model.py index 0ae0c1657..e1fd649ae 100644 --- a/pycalphad/model.py +++ b/pycalphad/model.py @@ -268,7 +268,7 @@ def unwrap_piecewise(graph): return graph.xreplace(replace_dict) for name, value in self.models.items(): - print(name, self.models[name].GM.atoms(Piecewise)) + print(name, self.models[name].atoms(Piecewise)) for _ in range(5): self.models[name] = unwrap_piecewise(self.models[name]) From 4a5e58735f989c0f9b850e4520df6271b6a7c581 Mon Sep 17 00:00:00 2001 From: Richard Otis Date: Fri, 14 Oct 2022 12:08:40 -0700 Subject: [PATCH 10/31] more debugging code --- pycalphad/model.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pycalphad/model.py b/pycalphad/model.py index e1fd649ae..9df195921 100644 --- a/pycalphad/model.py +++ b/pycalphad/model.py @@ -266,11 +266,12 @@ def unwrap_piecewise(graph): if len(args) == 4 and args[2] == 0 and args[3] == True and args[1].free_symbols == {v.T}: replace_dict[atom] = args[0] return graph.xreplace(replace_dict) - + print('------------------') + print(self.phase_name) for name, value in self.models.items(): - print(name, self.models[name].atoms(Piecewise)) for _ in range(5): self.models[name] = unwrap_piecewise(self.models[name]) + print(name, sorted(self.models[name].atoms(Piecewise))) self.site_fractions = sorted([x for x in self.variables if isinstance(x, v.SiteFraction)], key=str) self.state_variables = sorted([x for x in self.variables if not isinstance(x, v.SiteFraction)], key=str) From c38693f75c9830d55280a3b1625a79aaa63c2369 Mon Sep 17 00:00:00 2001 From: Richard Otis Date: Fri, 14 Oct 2022 12:23:06 -0700 Subject: [PATCH 11/31] debugging toggle verbosity for problem tests --- pycalphad/model.py | 4 +--- pycalphad/tests/test_equilibrium.py | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/pycalphad/model.py b/pycalphad/model.py index 9df195921..f4c666bf5 100644 --- a/pycalphad/model.py +++ b/pycalphad/model.py @@ -266,12 +266,10 @@ def unwrap_piecewise(graph): if len(args) == 4 and args[2] == 0 and args[3] == True and args[1].free_symbols == {v.T}: replace_dict[atom] = args[0] return graph.xreplace(replace_dict) - print('------------------') - print(self.phase_name) + for name, value in self.models.items(): for _ in range(5): self.models[name] = unwrap_piecewise(self.models[name]) - print(name, sorted(self.models[name].atoms(Piecewise))) self.site_fractions = sorted([x for x in self.variables if isinstance(x, v.SiteFraction)], key=str) self.state_variables = sorted([x for x in self.variables if not isinstance(x, v.SiteFraction)], key=str) diff --git a/pycalphad/tests/test_equilibrium.py b/pycalphad/tests/test_equilibrium.py index bd8db42a8..c4ad5f779 100644 --- a/pycalphad/tests/test_equilibrium.py +++ b/pycalphad/tests/test_equilibrium.py @@ -695,7 +695,7 @@ def test_eq_alni_low_temp(load_database): dbf = load_database() comps = ['AL', 'NI', 'VA'] phases = sorted(dbf.phases.keys()) - eq = equilibrium(dbf, comps, phases, {v.P: 101325, v.T: 300, v.N: 1, v.X('AL'): 0.4}) + eq = equilibrium(dbf, comps, phases, {v.P: 101325, v.T: 300, v.N: 1, v.X('AL'): 0.4}, verbose=True) # Verified in TC: https://github.com/pycalphad/pycalphad/pull/329#discussion_r637241358 assert_allclose(eq.GM.values, -63736.3048) assert_allclose(eq.MU.values.flatten(), [-116098.937755, -28827.882809]) @@ -715,7 +715,7 @@ def test_eq_alni_high_temp(load_database): dbf = load_database() comps = ['AL', 'NI', 'VA'] phases = sorted(dbf.phases.keys()) - eq = equilibrium(dbf, comps, phases, {v.P: 101325, v.T: 1600, v.N: 1, v.X('AL'): 0.65}) + eq = equilibrium(dbf, comps, phases, {v.P: 101325, v.T: 1600, v.N: 1, v.X('AL'): 0.65}, verbose=True) # if MIN_SITE_FRACTION is set to 1e-16: -131048.695 assert_allclose(eq.GM.values, -131081.998) # if MIN_SITE_FRACTION is set to 1e-16: [-106515.007322, -176611.259853] From 4eed79fc6fb010740a9ac27107608c6ae6b16f23 Mon Sep 17 00:00:00 2001 From: Richard Otis Date: Fri, 14 Oct 2022 12:55:05 -0700 Subject: [PATCH 12/31] debugging add energy test --- pycalphad/tests/test_calculate.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/pycalphad/tests/test_calculate.py b/pycalphad/tests/test_calculate.py index 1b6092718..c8004dbbd 100644 --- a/pycalphad/tests/test_calculate.py +++ b/pycalphad/tests/test_calculate.py @@ -269,4 +269,18 @@ def test_complex_multisublattice(): """ dbf = Database(tdb) calc_res = calculate(dbf, ['CR', 'FE', 'MO', 'NB', 'NI'], ['MU_PHASE'], N=1, P=101325, T=300, pdens=60) - assert calc_res.GM.size < 100000 \ No newline at end of file + assert calc_res.GM.size < 100000 + +@pytest.mark.solver +@select_database("alni_dupin_2001.tdb") +def test_calculation_jitter(load_database): + dbf = load_database() + comps = ['AL', 'NI', 'VA'] + phases = sorted(dbf.phases.keys()) + res = calculate(dbf, comps, phases, P=101325, T=1600, N=1, pdens=60) + with np.printoptions(threshold=np.inf): + print('-') + print(res.Phase.values) + print('-') + print(res.GM.values) + assert False \ No newline at end of file From e662deaba1bc1dc6438d92eadb6c1861a026c818 Mon Sep 17 00:00:00 2001 From: Richard Otis Date: Fri, 14 Oct 2022 13:22:07 -0700 Subject: [PATCH 13/31] debugging force lambda backend --- pycalphad/codegen/sympydiff_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pycalphad/codegen/sympydiff_utils.py b/pycalphad/codegen/sympydiff_utils.py index 92d1c48b9..864a9c288 100644 --- a/pycalphad/codegen/sympydiff_utils.py +++ b/pycalphad/codegen/sympydiff_utils.py @@ -35,7 +35,7 @@ BuildFunctionsResult = namedtuple('BuildFunctionsResult', ['func', 'grad', 'hess']) ConstraintFunctions = namedtuple('ConstraintFunctions', ['cons_func', 'cons_jac', 'cons_hess']) -if have_llvm: +if False:#have_llvm: LAMBDIFY_DEFAULT_BACKEND = 'llvm' else: LAMBDIFY_DEFAULT_BACKEND = 'lambda' From 4c51e1b797f226769d6099428e2070fd761d47dc Mon Sep 17 00:00:00 2001 From: Richard Otis Date: Fri, 14 Oct 2022 13:36:08 -0700 Subject: [PATCH 14/31] Debugging focus fcc_l12 phase --- pycalphad/codegen/sympydiff_utils.py | 2 +- pycalphad/tests/test_calculate.py | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/pycalphad/codegen/sympydiff_utils.py b/pycalphad/codegen/sympydiff_utils.py index 864a9c288..92d1c48b9 100644 --- a/pycalphad/codegen/sympydiff_utils.py +++ b/pycalphad/codegen/sympydiff_utils.py @@ -35,7 +35,7 @@ BuildFunctionsResult = namedtuple('BuildFunctionsResult', ['func', 'grad', 'hess']) ConstraintFunctions = namedtuple('ConstraintFunctions', ['cons_func', 'cons_jac', 'cons_hess']) -if False:#have_llvm: +if have_llvm: LAMBDIFY_DEFAULT_BACKEND = 'llvm' else: LAMBDIFY_DEFAULT_BACKEND = 'lambda' diff --git a/pycalphad/tests/test_calculate.py b/pycalphad/tests/test_calculate.py index c8004dbbd..37a7fd0fc 100644 --- a/pycalphad/tests/test_calculate.py +++ b/pycalphad/tests/test_calculate.py @@ -276,11 +276,10 @@ def test_complex_multisublattice(): def test_calculation_jitter(load_database): dbf = load_database() comps = ['AL', 'NI', 'VA'] - phases = sorted(dbf.phases.keys()) - res = calculate(dbf, comps, phases, P=101325, T=1600, N=1, pdens=60) + res = calculate(dbf, comps, ['FCC_L12'], P=101325, T=1600, N=1, pdens=60) with np.printoptions(threshold=np.inf): print('-') - print(res.Phase.values) + print(res.Y.values) print('-') print(res.GM.values) assert False \ No newline at end of file From cacd39ab147287f717ca9efe3c153b429b6ac1d6 Mon Sep 17 00:00:00 2001 From: Richard Otis Date: Fri, 14 Oct 2022 15:26:39 -0700 Subject: [PATCH 15/31] debugging piecewise atom level --- pycalphad/core/calculate.py | 16 +++++++++++++++- pycalphad/tests/test_calculate.py | 5 ----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/pycalphad/core/calculate.py b/pycalphad/core/calculate.py index a61215410..6ea981d4d 100644 --- a/pycalphad/core/calculate.py +++ b/pycalphad/core/calculate.py @@ -480,6 +480,8 @@ def calculate(dbf, comps, phases, mode=None, output='GM', fake_points=False, bro raise ValueError(f"model must contain a Model instance for every active phase. Missing Model objects for {sorted(active_phases_without_models)}") maximum_internal_dof = max(len(models[phase_name].site_fractions) for phase_name in active_phases) + from symengine import Piecewise + from itertools import chain for phase_name in sorted(active_phases): mod = models[phase_name] phase_record = phase_records[phase_name] @@ -488,7 +490,19 @@ def calculate(dbf, comps, phases, mode=None, output='GM', fake_points=False, bro points = _sample_phase_constitution(mod, sampler_dict[phase_name] or point_sample, fixedgrid_dict[phase_name], pdens_dict[phase_name]) points = np.atleast_2d(points) - + for atom in sorted(mod.GM.atoms(Piecewise), key=str): + for point_idx in range(points.shape[-2]): + dof = dict(zip(mod.variables, chain([1600., *points[..., point_idx, :]]))) + output_str = str(atom) + f'[{point_idx}] = (' + for arg in atom.args: + val = str(arg.subs(dof)) + output_str += val + output_str += ',' + output_str += ')' + print(output_str) + for point_idx in range(points.shape[-2]): + dof = dict(zip(mod.variables, chain([1600., *points[..., point_idx, :]]))) + print(f'[{point_idx}] = {dof}') fp = fake_points and (phase_name == sorted(active_phases)[0]) phase_ds = _compute_phase_values(nonvacant_components, str_statevar_dict, points, phase_record, output, diff --git a/pycalphad/tests/test_calculate.py b/pycalphad/tests/test_calculate.py index 37a7fd0fc..a6b99d436 100644 --- a/pycalphad/tests/test_calculate.py +++ b/pycalphad/tests/test_calculate.py @@ -277,9 +277,4 @@ def test_calculation_jitter(load_database): dbf = load_database() comps = ['AL', 'NI', 'VA'] res = calculate(dbf, comps, ['FCC_L12'], P=101325, T=1600, N=1, pdens=60) - with np.printoptions(threshold=np.inf): - print('-') - print(res.Y.values) - print('-') - print(res.GM.values) assert False \ No newline at end of file From 3d3a28d0469885e4df35ecb480e1b3ffa3ef6da1 Mon Sep 17 00:00:00 2001 From: Richard Otis Date: Fri, 14 Oct 2022 15:50:10 -0700 Subject: [PATCH 16/31] debugging fix --- pycalphad/core/calculate.py | 25 +++++++++++++------------ pycalphad/tests/test_calculate.py | 2 +- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/pycalphad/core/calculate.py b/pycalphad/core/calculate.py index 6ea981d4d..b00985e47 100644 --- a/pycalphad/core/calculate.py +++ b/pycalphad/core/calculate.py @@ -354,7 +354,7 @@ def _compute_phase_values(components, statevar_dict, return LightDataset(data_arrays, coords=coordinate_dict) -def calculate(dbf, comps, phases, mode=None, output='GM', fake_points=False, broadcast=True, parameters=None, to_xarray=True, phase_records=None, **kwargs): +def calculate(dbf, comps, phases, mode=None, output='GM', fake_points=False, broadcast=True, parameters=None, to_xarray=True, phase_records=None, _debug=False, **kwargs): """ Sample the property surface of 'output' containing the specified components and phases. Model parameters are taken from 'dbf' and any @@ -490,19 +490,20 @@ def calculate(dbf, comps, phases, mode=None, output='GM', fake_points=False, bro points = _sample_phase_constitution(mod, sampler_dict[phase_name] or point_sample, fixedgrid_dict[phase_name], pdens_dict[phase_name]) points = np.atleast_2d(points) - for atom in sorted(mod.GM.atoms(Piecewise), key=str): + if _debug: + for atom in sorted(mod.GM.atoms(Piecewise), key=str): + for point_idx in range(points.shape[-2]): + dof = dict(zip(mod.variables, chain([1600., *points[..., point_idx, :]]))) + output_str = str(atom) + f'[{point_idx}] = (' + for arg in atom.args: + val = str(arg.subs(dof)) + output_str += val + output_str += ',' + output_str += ')' + print(output_str) for point_idx in range(points.shape[-2]): dof = dict(zip(mod.variables, chain([1600., *points[..., point_idx, :]]))) - output_str = str(atom) + f'[{point_idx}] = (' - for arg in atom.args: - val = str(arg.subs(dof)) - output_str += val - output_str += ',' - output_str += ')' - print(output_str) - for point_idx in range(points.shape[-2]): - dof = dict(zip(mod.variables, chain([1600., *points[..., point_idx, :]]))) - print(f'[{point_idx}] = {dof}') + print(f'[{point_idx}] = {dof}') fp = fake_points and (phase_name == sorted(active_phases)[0]) phase_ds = _compute_phase_values(nonvacant_components, str_statevar_dict, points, phase_record, output, diff --git a/pycalphad/tests/test_calculate.py b/pycalphad/tests/test_calculate.py index a6b99d436..77a935527 100644 --- a/pycalphad/tests/test_calculate.py +++ b/pycalphad/tests/test_calculate.py @@ -276,5 +276,5 @@ def test_complex_multisublattice(): def test_calculation_jitter(load_database): dbf = load_database() comps = ['AL', 'NI', 'VA'] - res = calculate(dbf, comps, ['FCC_L12'], P=101325, T=1600, N=1, pdens=60) + res = calculate(dbf, comps, ['FCC_L12'], P=101325, T=1600, N=1, pdens=60, _debug=True) assert False \ No newline at end of file From 5e54abd5440889cba4cbcf977375c6fed35cc849 Mon Sep 17 00:00:00 2001 From: Richard Otis Date: Fri, 14 Oct 2022 16:42:38 -0700 Subject: [PATCH 17/31] debugging force numerical result --- pycalphad/core/calculate.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pycalphad/core/calculate.py b/pycalphad/core/calculate.py index b00985e47..f868bea48 100644 --- a/pycalphad/core/calculate.py +++ b/pycalphad/core/calculate.py @@ -496,10 +496,10 @@ def calculate(dbf, comps, phases, mode=None, output='GM', fake_points=False, bro dof = dict(zip(mod.variables, chain([1600., *points[..., point_idx, :]]))) output_str = str(atom) + f'[{point_idx}] = (' for arg in atom.args: - val = str(arg.subs(dof)) - output_str += val + val = arg.subs(dof) + output_str += str(val) output_str += ',' - output_str += ')' + output_str += ') = ' + str(atom.subs(dof).evalf(real=True)) print(output_str) for point_idx in range(points.shape[-2]): dof = dict(zip(mod.variables, chain([1600., *points[..., point_idx, :]]))) From 9fc9bc5ed694b77f412053b2164b38c12319b8c0 Mon Sep 17 00:00:00 2001 From: Richard Otis Date: Fri, 14 Oct 2022 17:11:39 -0700 Subject: [PATCH 18/31] debugging switch to energy level evaluation --- pycalphad/core/calculate.py | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/pycalphad/core/calculate.py b/pycalphad/core/calculate.py index f868bea48..8dc15e289 100644 --- a/pycalphad/core/calculate.py +++ b/pycalphad/core/calculate.py @@ -490,25 +490,26 @@ def calculate(dbf, comps, phases, mode=None, output='GM', fake_points=False, bro points = _sample_phase_constitution(mod, sampler_dict[phase_name] or point_sample, fixedgrid_dict[phase_name], pdens_dict[phase_name]) points = np.atleast_2d(points) - if _debug: - for atom in sorted(mod.GM.atoms(Piecewise), key=str): - for point_idx in range(points.shape[-2]): - dof = dict(zip(mod.variables, chain([1600., *points[..., point_idx, :]]))) - output_str = str(atom) + f'[{point_idx}] = (' - for arg in atom.args: - val = arg.subs(dof) - output_str += str(val) - output_str += ',' - output_str += ') = ' + str(atom.subs(dof).evalf(real=True)) - print(output_str) - for point_idx in range(points.shape[-2]): - dof = dict(zip(mod.variables, chain([1600., *points[..., point_idx, :]]))) - print(f'[{point_idx}] = {dof}') + fp = fake_points and (phase_name == sorted(active_phases)[0]) phase_ds = _compute_phase_values(nonvacant_components, str_statevar_dict, points, phase_record, output, maximum_internal_dof, broadcast=broadcast, parameters=parameters, largest_energy=float(largest_energy), fake_points=fp) + if _debug: + #for atom in sorted(mod.GM.atoms(Piecewise), key=str): + # for point_idx in range(points.shape[-2]): + # dof = dict(zip(mod.variables, chain([1600., *points[..., point_idx, :]]))) + # output_str = str(atom) + f'[{point_idx}] = (' + # for arg in atom.args: + # val = arg.subs(dof) + # output_str += str(val) + # output_str += ',' + # output_str += ') = ' + str(atom.subs(dof).evalf(real=True)) + # print(output_str) + for point_idx in range(points.shape[-2]): + dof = dict(zip(mod.variables, chain([1600., *points[..., point_idx, :]]))) + print(f'[{point_idx}] = {phase_ds.GM.flat[point_idx]} = {mod.GM.subs(dof).evalf(real=True)}') all_phase_data.append(phase_ds) fp_offset = len(nonvacant_elements) if fake_points else 0 From ddfa33bdb429342efd8588236745c9f0e208d7f0 Mon Sep 17 00:00:00 2001 From: Richard Otis Date: Fri, 14 Oct 2022 17:59:21 -0700 Subject: [PATCH 19/31] WIP: attempt to fix numerical difference through forced type conversion --- pycalphad/core/calculate.py | 16 +--------------- pycalphad/model.py | 7 ++++++- pycalphad/tests/test_calculate.py | 10 ++++++++-- 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/pycalphad/core/calculate.py b/pycalphad/core/calculate.py index 8dc15e289..ce0f4dd42 100644 --- a/pycalphad/core/calculate.py +++ b/pycalphad/core/calculate.py @@ -354,7 +354,7 @@ def _compute_phase_values(components, statevar_dict, return LightDataset(data_arrays, coords=coordinate_dict) -def calculate(dbf, comps, phases, mode=None, output='GM', fake_points=False, broadcast=True, parameters=None, to_xarray=True, phase_records=None, _debug=False, **kwargs): +def calculate(dbf, comps, phases, mode=None, output='GM', fake_points=False, broadcast=True, parameters=None, to_xarray=True, phase_records=None, **kwargs): """ Sample the property surface of 'output' containing the specified components and phases. Model parameters are taken from 'dbf' and any @@ -496,20 +496,6 @@ def calculate(dbf, comps, phases, mode=None, output='GM', fake_points=False, bro points, phase_record, output, maximum_internal_dof, broadcast=broadcast, parameters=parameters, largest_energy=float(largest_energy), fake_points=fp) - if _debug: - #for atom in sorted(mod.GM.atoms(Piecewise), key=str): - # for point_idx in range(points.shape[-2]): - # dof = dict(zip(mod.variables, chain([1600., *points[..., point_idx, :]]))) - # output_str = str(atom) + f'[{point_idx}] = (' - # for arg in atom.args: - # val = arg.subs(dof) - # output_str += str(val) - # output_str += ',' - # output_str += ') = ' + str(atom.subs(dof).evalf(real=True)) - # print(output_str) - for point_idx in range(points.shape[-2]): - dof = dict(zip(mod.variables, chain([1600., *points[..., point_idx, :]]))) - print(f'[{point_idx}] = {phase_ds.GM.flat[point_idx]} = {mod.GM.subs(dof).evalf(real=True)}') all_phase_data.append(phase_ds) fp_offset = len(nonvacant_elements) if fake_points else 0 diff --git a/pycalphad/model.py b/pycalphad/model.py index f4c666bf5..590aff3b0 100644 --- a/pycalphad/model.py +++ b/pycalphad/model.py @@ -264,7 +264,12 @@ def unwrap_piecewise(graph): args = atom.args # Unwrap temperature-dependent piecewise with zero-defaults if len(args) == 4 and args[2] == 0 and args[3] == True and args[1].free_symbols == {v.T}: - replace_dict[atom] = args[0] + arg0 = args[0] + try: + arg0 = float(arg0) + except (ValueError, RuntimeError): + pass + replace_dict[atom] = arg0 return graph.xreplace(replace_dict) for name, value in self.models.items(): diff --git a/pycalphad/tests/test_calculate.py b/pycalphad/tests/test_calculate.py index 77a935527..f52dcf0de 100644 --- a/pycalphad/tests/test_calculate.py +++ b/pycalphad/tests/test_calculate.py @@ -5,6 +5,7 @@ import pytest from pycalphad import Database, calculate, Model, variables as v +from itertools import chain import numpy as np from numpy.testing import assert_allclose from pycalphad.codegen.callables import build_phase_records @@ -274,7 +275,12 @@ def test_complex_multisublattice(): @pytest.mark.solver @select_database("alni_dupin_2001.tdb") def test_calculation_jitter(load_database): + "Platform-dependent numerical differences stemming from optimizations of the Model object representation (gh-431)" dbf = load_database() comps = ['AL', 'NI', 'VA'] - res = calculate(dbf, comps, ['FCC_L12'], P=101325, T=1600, N=1, pdens=60, _debug=True) - assert False \ No newline at end of file + res = calculate(dbf, comps, ['FCC_L12'], P=101325, T=1600, N=1, pdens=60) + points = res.Y.values + mod = Model(dbf, comps, 'FCC_L12') + for point_idx in range(points.shape[-2]): + dof = dict(zip(mod.variables, chain([1600., *points[..., point_idx, :].flat]))) + np.testing.assert_allclose(res.GM.values.flat[point_idx], float(mod.GM.subs(dof).evalf(real=True))) From 644d3df4d9fc676ebe8bd31cf66e8e5b1afe0965 Mon Sep 17 00:00:00 2001 From: Richard Otis Date: Fri, 14 Oct 2022 18:13:02 -0700 Subject: [PATCH 20/31] debugging see if disordered fcc has the same issue --- pycalphad/tests/test_calculate.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pycalphad/tests/test_calculate.py b/pycalphad/tests/test_calculate.py index f52dcf0de..8f709fccb 100644 --- a/pycalphad/tests/test_calculate.py +++ b/pycalphad/tests/test_calculate.py @@ -278,9 +278,9 @@ def test_calculation_jitter(load_database): "Platform-dependent numerical differences stemming from optimizations of the Model object representation (gh-431)" dbf = load_database() comps = ['AL', 'NI', 'VA'] - res = calculate(dbf, comps, ['FCC_L12'], P=101325, T=1600, N=1, pdens=60) + res = calculate(dbf, comps, ['FCC_A1'], P=101325, T=1600, N=1, pdens=60) points = res.Y.values - mod = Model(dbf, comps, 'FCC_L12') + mod = Model(dbf, comps, 'FCC_A1') for point_idx in range(points.shape[-2]): dof = dict(zip(mod.variables, chain([1600., *points[..., point_idx, :].flat]))) np.testing.assert_allclose(res.GM.values.flat[point_idx], float(mod.GM.subs(dof).evalf(real=True))) From 82ec8169e82dc70cde46359858edf68d0e6b0a8c Mon Sep 17 00:00:00 2001 From: Richard Otis Date: Fri, 14 Oct 2022 18:23:26 -0700 Subject: [PATCH 21/31] WIP: Try not replacing the ordering contribution --- pycalphad/core/calculate.py | 3 +-- pycalphad/model.py | 2 ++ pycalphad/tests/test_calculate.py | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pycalphad/core/calculate.py b/pycalphad/core/calculate.py index ce0f4dd42..77b28ee11 100644 --- a/pycalphad/core/calculate.py +++ b/pycalphad/core/calculate.py @@ -480,8 +480,7 @@ def calculate(dbf, comps, phases, mode=None, output='GM', fake_points=False, bro raise ValueError(f"model must contain a Model instance for every active phase. Missing Model objects for {sorted(active_phases_without_models)}") maximum_internal_dof = max(len(models[phase_name].site_fractions) for phase_name in active_phases) - from symengine import Piecewise - from itertools import chain + for phase_name in sorted(active_phases): mod = models[phase_name] phase_record = phase_records[phase_name] diff --git a/pycalphad/model.py b/pycalphad/model.py index 590aff3b0..27926c12c 100644 --- a/pycalphad/model.py +++ b/pycalphad/model.py @@ -273,6 +273,8 @@ def unwrap_piecewise(graph): return graph.xreplace(replace_dict) for name, value in self.models.items(): + if name == 'ord': + continue for _ in range(5): self.models[name] = unwrap_piecewise(self.models[name]) diff --git a/pycalphad/tests/test_calculate.py b/pycalphad/tests/test_calculate.py index 8f709fccb..f52dcf0de 100644 --- a/pycalphad/tests/test_calculate.py +++ b/pycalphad/tests/test_calculate.py @@ -278,9 +278,9 @@ def test_calculation_jitter(load_database): "Platform-dependent numerical differences stemming from optimizations of the Model object representation (gh-431)" dbf = load_database() comps = ['AL', 'NI', 'VA'] - res = calculate(dbf, comps, ['FCC_A1'], P=101325, T=1600, N=1, pdens=60) + res = calculate(dbf, comps, ['FCC_L12'], P=101325, T=1600, N=1, pdens=60) points = res.Y.values - mod = Model(dbf, comps, 'FCC_A1') + mod = Model(dbf, comps, 'FCC_L12') for point_idx in range(points.shape[-2]): dof = dict(zip(mod.variables, chain([1600., *points[..., point_idx, :].flat]))) np.testing.assert_allclose(res.GM.values.flat[point_idx], float(mod.GM.subs(dof).evalf(real=True))) From b5be57cd388b9b30b9ea2c51243560451e5fcae5 Mon Sep 17 00:00:00 2001 From: Richard Otis Date: Fri, 14 Oct 2022 18:41:42 -0700 Subject: [PATCH 22/31] WIP: Try unwrapping symbols only --- pycalphad/model.py | 37 +++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/pycalphad/model.py b/pycalphad/model.py index 27926c12c..2fe67306c 100644 --- a/pycalphad/model.py +++ b/pycalphad/model.py @@ -258,29 +258,24 @@ def __init__(self, dbe, comps, phase_name, parameters=None): # 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) - def unwrap_piecewise(graph): - replace_dict = {} - for atom in graph.atoms(Piecewise): - args = atom.args - # Unwrap temperature-dependent piecewise with zero-defaults - if len(args) == 4 and args[2] == 0 and args[3] == True and args[1].free_symbols == {v.T}: - arg0 = args[0] - try: - arg0 = float(arg0) - except (ValueError, RuntimeError): - pass - replace_dict[atom] = arg0 - return graph.xreplace(replace_dict) - - for name, value in self.models.items(): - if name == 'ord': - continue - for _ in range(5): - self.models[name] = unwrap_piecewise(self.models[name]) - self.site_fractions = sorted([x for x in self.variables if isinstance(x, v.SiteFraction)], key=str) self.state_variables = sorted([x for x in self.variables if not isinstance(x, v.SiteFraction)], key=str) + @staticmethod + def unwrap_piecewise(graph): + replace_dict = {} + for atom in graph.atoms(Piecewise): + args = atom.args + # Unwrap temperature-dependent piecewise with zero-defaults + if len(args) == 4 and args[2] == 0 and args[3] == True and args[1].free_symbols == {v.T}: + arg0 = args[0] + try: + arg0 = float(arg0) + except (ValueError, RuntimeError): + pass + replace_dict[atom] = arg0 + return graph.xreplace(replace_dict) + @staticmethod def symbol_replace(obj, symbols): """ @@ -300,6 +295,8 @@ def symbol_replace(obj, symbols): # of other symbols for iteration in range(_MAX_PARAM_NESTING): obj = obj.xreplace(symbols) + if iteration < 3: + obj = Model.unwrap_piecewise(obj) undefs = [x for x in obj.free_symbols if not isinstance(x, v.StateVariable)] if len(undefs) == 0: break From 771564fe2369575a3c2c75b8702c480df53259ba Mon Sep 17 00:00:00 2001 From: Richard Otis Date: Sat, 15 Oct 2022 09:28:33 -0700 Subject: [PATCH 23/31] BLD: deploy: Bump versions for wheel build/deploy --- .github/workflows/deploy.yaml | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 9d721a53e..72ec876ae 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -16,20 +16,15 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-20.04, windows-2019, macos-10.15] + os: [ubuntu-20.04, windows-2019, macos-11] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: fetch-depth: 0 # fetch the entire repo history, required to guarantee versioneer will pick up the tags - - uses: actions/setup-python@v2 - name: Install Python - with: - python-version: '3.8' - - name: Build wheels - uses: pypa/cibuildwheel@v2.3.1 + uses: pypa/cibuildwheel@v2.11.1 env: # Build all CPython platforms except Python 3.5 and 3.6 # PyPy wheels not allowed because SciPy (build requirement) is not available @@ -40,7 +35,7 @@ jobs: CIBW_ARCHS_WINDOWS: auto64 CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014 - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: path: ./wheelhouse/*.whl @@ -48,7 +43,7 @@ jobs: name: Build source distribution runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: actions/setup-python@v2 name: Install Python @@ -60,7 +55,7 @@ jobs: - name: Build sdist run: python -m build --sdist - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: path: dist/*.tar.gz From aa279f2cf42102d0f10dfc9e50dac4a8e4835ecb Mon Sep 17 00:00:00 2001 From: Richard Otis Date: Sat, 15 Oct 2022 09:29:43 -0700 Subject: [PATCH 24/31] TST: test_calculate: Change test name --- pycalphad/tests/test_calculate.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pycalphad/tests/test_calculate.py b/pycalphad/tests/test_calculate.py index f52dcf0de..c451ba7ca 100644 --- a/pycalphad/tests/test_calculate.py +++ b/pycalphad/tests/test_calculate.py @@ -274,7 +274,7 @@ def test_complex_multisublattice(): @pytest.mark.solver @select_database("alni_dupin_2001.tdb") -def test_calculation_jitter(load_database): +def test_calculation_symengine_evalf_energy_difference(load_database): "Platform-dependent numerical differences stemming from optimizations of the Model object representation (gh-431)" dbf = load_database() comps = ['AL', 'NI', 'VA'] From a54faec328d5d9103b03035dbd3b8416f6e0e286 Mon Sep 17 00:00:00 2001 From: Richard Otis Date: Sat, 15 Oct 2022 09:31:21 -0700 Subject: [PATCH 25/31] MAINT: test_equilibrium: Remove debug verbosity from equilibrium calls --- pycalphad/tests/test_equilibrium.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pycalphad/tests/test_equilibrium.py b/pycalphad/tests/test_equilibrium.py index c4ad5f779..bd8db42a8 100644 --- a/pycalphad/tests/test_equilibrium.py +++ b/pycalphad/tests/test_equilibrium.py @@ -695,7 +695,7 @@ def test_eq_alni_low_temp(load_database): dbf = load_database() comps = ['AL', 'NI', 'VA'] phases = sorted(dbf.phases.keys()) - eq = equilibrium(dbf, comps, phases, {v.P: 101325, v.T: 300, v.N: 1, v.X('AL'): 0.4}, verbose=True) + eq = equilibrium(dbf, comps, phases, {v.P: 101325, v.T: 300, v.N: 1, v.X('AL'): 0.4}) # Verified in TC: https://github.com/pycalphad/pycalphad/pull/329#discussion_r637241358 assert_allclose(eq.GM.values, -63736.3048) assert_allclose(eq.MU.values.flatten(), [-116098.937755, -28827.882809]) @@ -715,7 +715,7 @@ def test_eq_alni_high_temp(load_database): dbf = load_database() comps = ['AL', 'NI', 'VA'] phases = sorted(dbf.phases.keys()) - eq = equilibrium(dbf, comps, phases, {v.P: 101325, v.T: 1600, v.N: 1, v.X('AL'): 0.65}, verbose=True) + eq = equilibrium(dbf, comps, phases, {v.P: 101325, v.T: 1600, v.N: 1, v.X('AL'): 0.65}) # if MIN_SITE_FRACTION is set to 1e-16: -131048.695 assert_allclose(eq.GM.values, -131081.998) # if MIN_SITE_FRACTION is set to 1e-16: [-106515.007322, -176611.259853] From aadfda3cb43c1938b27f702bf9532fd69b3399c3 Mon Sep 17 00:00:00 2001 From: Richard Otis Date: Sat, 18 Feb 2023 10:25:56 -0600 Subject: [PATCH 26/31] MAINT: Model: Remove unnecessary float coercion code --- pycalphad/model.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/pycalphad/model.py b/pycalphad/model.py index 2fe67306c..ff7de9288 100644 --- a/pycalphad/model.py +++ b/pycalphad/model.py @@ -268,12 +268,7 @@ def unwrap_piecewise(graph): args = atom.args # Unwrap temperature-dependent piecewise with zero-defaults if len(args) == 4 and args[2] == 0 and args[3] == True and args[1].free_symbols == {v.T}: - arg0 = args[0] - try: - arg0 = float(arg0) - except (ValueError, RuntimeError): - pass - replace_dict[atom] = arg0 + replace_dict[atom] = args[0] return graph.xreplace(replace_dict) @staticmethod From fb140a21020a4f74fafb9129ebfffd66fe8a2126 Mon Sep 17 00:00:00 2001 From: Richard Otis Date: Sat, 18 Feb 2023 11:01:04 -0600 Subject: [PATCH 27/31] MAINT: Model: Try removing iteration count limit for Piecewise unwrapping --- pycalphad/model.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pycalphad/model.py b/pycalphad/model.py index ff7de9288..5d1ac130a 100644 --- a/pycalphad/model.py +++ b/pycalphad/model.py @@ -290,8 +290,7 @@ def symbol_replace(obj, symbols): # of other symbols for iteration in range(_MAX_PARAM_NESTING): obj = obj.xreplace(symbols) - if iteration < 3: - obj = Model.unwrap_piecewise(obj) + obj = Model.unwrap_piecewise(obj) undefs = [x for x in obj.free_symbols if not isinstance(x, v.StateVariable)] if len(undefs) == 0: break From 0cafa393d0550584a721f862fece11020c43e934 Mon Sep 17 00:00:00 2001 From: Richard Otis Date: Thu, 6 Apr 2023 17:13:46 -0500 Subject: [PATCH 28/31] FIX: calculate: Still add some extra_points for very complex phases --- pycalphad/core/calculate.py | 15 ++++++--------- pycalphad/core/constants.py | 5 +++++ 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/pycalphad/core/calculate.py b/pycalphad/core/calculate.py index 77b28ee11..690f4d62c 100644 --- a/pycalphad/core/calculate.py +++ b/pycalphad/core/calculate.py @@ -19,7 +19,7 @@ from pycalphad.core.utils import endmember_matrix, extract_parameters, \ get_pure_elements, filter_phases, instantiate_models, point_sample, \ unpack_components, unpack_condition, unpack_kwarg -from pycalphad.core.constants import MIN_SITE_FRACTION +from pycalphad.core.constants import MIN_SITE_FRACTION, MAX_ENDMEMBER_PAIRS, MAX_EXTRA_POINTS def hr_point_sample(constraint_jac, constraint_rhs, initial_point, num_points): @@ -150,14 +150,11 @@ def _sample_phase_constitution(model, sampler, fixed_grid, pdens): if (fixed_grid is True) and not linearly_constrained_space: # Sample along the edges of the endmembers # These constitution space edges are often the equilibrium points! - em_pairs = list(itertools.combinations(points, 2)) - lingrid = np.linspace(0, 1, pdens) - if len(em_pairs) * pdens < 100000: - extra_points = [first_em * lingrid[np.newaxis].T + - second_em * lingrid[::-1][np.newaxis].T - for first_em, second_em in em_pairs] - else: - extra_points = [] + em_pairs = list(itertools.combinations(points, 2))[:MAX_ENDMEMBER_PAIRS] + lingrid = np.linspace(0, 1, int(min(pdens, MAX_EXTRA_POINTS/MAX_ENDMEMBER_PAIRS))) + extra_points = [first_em * lingrid[np.newaxis].T + + second_em * lingrid[::-1][np.newaxis].T + for first_em, second_em in em_pairs] points = np.concatenate(list(itertools.chain([points], extra_points))) # Sample composition space for more points if sum(sublattice_dof) > len(sublattice_dof): diff --git a/pycalphad/core/constants.py b/pycalphad/core/constants.py index d8a6b449f..af77731d0 100644 --- a/pycalphad/core/constants.py +++ b/pycalphad/core/constants.py @@ -12,3 +12,8 @@ # Constraint scaling factors, for numerical stability INTERNAL_CONSTRAINT_SCALING = 1.0 + +# Prevent excessive sampling for very complex phase models +# This avoids running out of RAM +MAX_ENDMEMBER_PAIRS = 5000 # ~100 endmembers +MAX_EXTRA_POINTS = 90000 From acdb576a2304b8fcdb949723281e03cbfbbcb6e4 Mon Sep 17 00:00:00 2001 From: Richard Otis Date: Sun, 7 May 2023 14:58:08 -0500 Subject: [PATCH 29/31] FIX: calculate: Compute lingrid based on actual number of endmember pairs, not the max --- pycalphad/core/calculate.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pycalphad/core/calculate.py b/pycalphad/core/calculate.py index 690f4d62c..97f21c6f0 100644 --- a/pycalphad/core/calculate.py +++ b/pycalphad/core/calculate.py @@ -151,11 +151,12 @@ def _sample_phase_constitution(model, sampler, fixed_grid, pdens): # Sample along the edges of the endmembers # These constitution space edges are often the equilibrium points! em_pairs = list(itertools.combinations(points, 2))[:MAX_ENDMEMBER_PAIRS] - lingrid = np.linspace(0, 1, int(min(pdens, MAX_EXTRA_POINTS/MAX_ENDMEMBER_PAIRS))) - extra_points = [first_em * lingrid[np.newaxis].T + - second_em * lingrid[::-1][np.newaxis].T - for first_em, second_em in em_pairs] - points = np.concatenate(list(itertools.chain([points], extra_points))) + if len(em_pairs) > 0: + lingrid = np.linspace(0, 1, int(min(pdens, MAX_EXTRA_POINTS/len(em_pairs)))) + extra_points = [first_em * lingrid[np.newaxis].T + + second_em * lingrid[::-1][np.newaxis].T + for first_em, second_em in em_pairs] + points = np.concatenate(list(itertools.chain([points], extra_points))) # Sample composition space for more points if sum(sublattice_dof) > len(sublattice_dof): if linearly_constrained_space: From 0701ee6b4f4658dd50474a16d7981bc0d83e95c3 Mon Sep 17 00:00:00 2001 From: Richard Otis Date: Thu, 10 Aug 2023 11:31:46 -0500 Subject: [PATCH 30/31] ENH/FIX: Model: Extrapolate beyond temperature limits by default --- pycalphad/model.py | 50 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/pycalphad/model.py b/pycalphad/model.py index a65f3d1ea..7dcf327ff 100644 --- a/pycalphad/model.py +++ b/pycalphad/model.py @@ -97,6 +97,12 @@ def __repr__(self): s = "ReferenceState('{}', '{}')".format(self.species.name, self.phase_name) return s +class classproperty(object): + # https://stackoverflow.com/questions/5189699/how-to-make-a-class-property + def __init__(self, f): + self.f = f + def __get__(self, obj, owner): + return self.f(owner) class Model(object): """ @@ -154,6 +160,13 @@ class Model(object): ('2st', 'twostate_energy'), ('ein', 'einstein_energy'), ('vol', 'volume_energy'), ('ord', 'atomic_ordering_energy')] + # Behave as if piecewise temperature bounds extend to +-inf (i.e., ignore lower/upper T limits for parameters) + # This follows the behavior of most commercial codes, but may be undesirable in some circumstances + # Designed to be readonly on instances, because mutation after initialization will not work + @classproperty + def extrapolate_temperature_bounds(cls): + return True + def __new__(cls, *args, **kwargs): target_cls = cls._dispatch_on(*args, **kwargs) instance = object.__new__(target_cls) @@ -261,18 +274,45 @@ def __init__(self, dbe, comps, phase_name, parameters=None): self.site_fractions = sorted([x for x in self.variables if isinstance(x, v.SiteFraction)], key=str) self.state_variables = sorted([x for x in self.variables if not isinstance(x, v.SiteFraction)], key=str) - @staticmethod - def unwrap_piecewise(graph): + @classmethod + def unwrap_piecewise(cls, graph): + from pycalphad.io.tdb import to_interval replace_dict = {} for atom in graph.atoms(Piecewise): args = atom.args # Unwrap temperature-dependent piecewise with zero-defaults if len(args) == 4 and args[2] == 0 and args[3] == True and args[1].free_symbols == {v.T}: replace_dict[atom] = args[0] + elif (len(args) > 4) and cls.extrapolate_temperature_bounds: + # Set lower and upper temperature limits to -+infinity + # First filter out default zero-branches + filtered_args = [(x, cond) for x, cond in zip(*[iter(args)]*2) if not ((cond == S.true) and (x == S.Zero))] + if not all([cond.free_symbols == {v.T} for _, cond in filtered_args]): + # Only temperature-dependent piecewise conditions are supported for extrapolation + continue + intervals = [to_interval(cond) for _, cond in filtered_args] + sortindices = [i[0] for i in sorted(enumerate(intervals), key=lambda x:x[1].args[0])] + if (intervals[sortindices[0]].args[0] == S.NegativeInfinity) and \ + (intervals[sortindices[-1]].args[1] == S.Infinity): + # Nothing to do, temperature range already extrapolated + continue + # First branch is special-cased to negative infinity + exprcondpairs = [(filtered_args[sortindices[0]][0], v.T < intervals[sortindices[0]].args[1])] + for idx in sortindices[1:-1]: + exprcondpairs.append((filtered_args[sortindices[idx]][0], + And(v.T >= intervals[sortindices[idx]].args[0], v.T < intervals[sortindices[idx]].args[1]) + )) + # Last branch is special-cased to positive infinity + exprcondpairs.append((filtered_args[sortindices[-1]][0], + v.T >= intervals[sortindices[-1]].args[0] + )) + # Branch required for LLVM (should never hit in this formulation) + exprcondpairs.append((0, True)) + replace_dict[atom] = Piecewise(*exprcondpairs) return graph.xreplace(replace_dict) - @staticmethod - def symbol_replace(obj, symbols): + @classmethod + def symbol_replace(cls, obj, symbols): """ Substitute values of symbols into 'obj'. @@ -290,7 +330,7 @@ def symbol_replace(obj, symbols): # of other symbols for iteration in range(_MAX_PARAM_NESTING): obj = obj.xreplace(symbols) - obj = Model.unwrap_piecewise(obj) + obj = cls.unwrap_piecewise(obj) undefs = [x for x in obj.free_symbols if not isinstance(x, v.StateVariable)] if len(undefs) == 0: break From 35413973d593fa0a12777657ff1619c4676e401c Mon Sep 17 00:00:00 2001 From: Richard Otis Date: Thu, 10 Aug 2023 19:32:46 -0500 Subject: [PATCH 31/31] TST/FIX: extrapolate_temperature: Confirm Model extrapolation works as expected --- pycalphad/model.py | 6 +++-- pycalphad/tests/test_model.py | 48 +++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/pycalphad/model.py b/pycalphad/model.py index 7dcf327ff..2ccf88651 100644 --- a/pycalphad/model.py +++ b/pycalphad/model.py @@ -283,10 +283,12 @@ def unwrap_piecewise(cls, graph): # Unwrap temperature-dependent piecewise with zero-defaults if len(args) == 4 and args[2] == 0 and args[3] == True and args[1].free_symbols == {v.T}: replace_dict[atom] = args[0] - elif (len(args) > 4) and cls.extrapolate_temperature_bounds: + elif cls.extrapolate_temperature_bounds: # Set lower and upper temperature limits to -+infinity # First filter out default zero-branches filtered_args = [(x, cond) for x, cond in zip(*[iter(args)]*2) if not ((cond == S.true) and (x == S.Zero))] + if len(filtered_args) == 0: + continue if not all([cond.free_symbols == {v.T} for _, cond in filtered_args]): # Only temperature-dependent piecewise conditions are supported for extrapolation continue @@ -306,7 +308,7 @@ def unwrap_piecewise(cls, graph): exprcondpairs.append((filtered_args[sortindices[-1]][0], v.T >= intervals[sortindices[-1]].args[0] )) - # Branch required for LLVM (should never hit in this formulation) + # Catch-all branch required for LLVM (should never hit in this formulation) exprcondpairs.append((0, True)) replace_dict[atom] = Piecewise(*exprcondpairs) return graph.xreplace(replace_dict) diff --git a/pycalphad/tests/test_model.py b/pycalphad/tests/test_model.py index dec4adcf1..005d64a04 100644 --- a/pycalphad/tests/test_model.py +++ b/pycalphad/tests/test_model.py @@ -173,3 +173,51 @@ def test_model_deep_branching(load_database): # However, that is a relatively long test. This just checks that the deep branches were cleaned up. # Without optimization/unwrapping, this would be about 57 assert len(mod.GM.atoms(Piecewise)) < 30 + +def test_model_extrapolate_temperature(): + "Models extrapolate temperature bounds outside upper/lower limits" + TDB_extrapolate = """ + ELEMENT VA VACUUM 0.0000E+00 0.0000E+00 0.0000E+00 ! + ELEMENT A DISORD 0.0000E+00 0.0000E+00 0.0000E+00 ! + ELEMENT B DISORD 0.0000E+00 0.0000E+00 0.0000E+00 ! + + FUNCTION GSYM1 500 +100; 5000.00 Y 40000; 10000 N ! + + DEFINE_SYSTEM_DEFAULT ELEMENT 2 ! + DEFAULT_COMMAND DEF_SYS_ELEMENT VA ! + + TYPE_DEFINITION % SEQ *! + TYPE_DEFINITION ' GES A_P_D ORDERED DIS_PART DISORD ,,,! + + PHASE DISORD % 2 1 3 ! + PHASE ORDERED %' 3 0.5 0.5 3 ! + + CONSTITUENT DISORD : A,B : VA : ! + CONSTITUENT ORDERED : A,B: A,B : VA : ! + + PARAMETER G(DISORD,A:VA;0) 298.15 -10000+GSYM1#; 1000 Y -3000-GSYM1#; 6000 N ! + PARAMETER G(DISORD,B:VA;0) 298.15 -10000+GSYM1#; 1000 Y -3000-GSYM1#; 6000 N ! + + """ + dbf = Database(TDB_extrapolate) + # First, confirm that turning the extrapolation off reproduces the correct behavior + class modtype(Model): + extrapolate_temperature_bounds = False + mod = modtype(dbf, ['A', 'B', 'VA'], 'ORDERED') + # Remove ideal mixing effects so we can test extrapolation easily + mod.models['idmix'] = 0 + dof = {v.Y('ORDERED', 0, 'A'): 0.5, v.Y('ORDERED', 0, 'B'): 0.5, + v.Y('ORDERED', 1, 'A'): 0.5, v.Y('ORDERED', 1, 'B'): 0.5, + v.Y('ORDERED', 2, 'VA'): 1.0} + assert mod.GM.subs(dof).subs({v.T: 200}).n(real=True) == 0. + assert mod.GM.subs(dof).subs({v.T: 50000}).n(real=True) == 0. + + # Next, test that the default behavior (extrapolation) works as intended + mod = Model(dbf, ['A', 'B', 'VA'], 'ORDERED') + # Remove ideal mixing effects so we can test extrapolation easily + mod.models['idmix'] = 0 + dof = {v.Y('ORDERED', 0, 'A'): 0.5, v.Y('ORDERED', 0, 'B'): 0.5, + v.Y('ORDERED', 1, 'A'): 0.5, v.Y('ORDERED', 1, 'B'): 0.5, + v.Y('ORDERED', 2, 'VA'): 1.0} + assert mod.GM.subs(dof).subs({v.T: 200}).n(real=True) == -9900. + assert mod.GM.subs(dof).subs({v.T: 50000}).n(real=True) == -43000.