From f66ec11d9b42e81a269e2a71f64df545cc754286 Mon Sep 17 00:00:00 2001 From: Nicholas Ury Date: Wed, 25 Dec 2024 16:22:54 -0800 Subject: [PATCH] additional tests --- pycalphad/mapping/strategy/step_strategy.py | 7 ++- pycalphad/mapping/strategy/strategy_base.py | 6 +- pycalphad/tests/test_mapping_strategy.py | 68 +++++++++++++++++++-- pycalphad/tests/test_variables.py | 22 ++++++- pycalphad/variables.py | 3 + 5 files changed, 95 insertions(+), 11 deletions(-) diff --git a/pycalphad/mapping/strategy/step_strategy.py b/pycalphad/mapping/strategy/step_strategy.py index 774e86a3d..88468e0e0 100644 --- a/pycalphad/mapping/strategy/step_strategy.py +++ b/pycalphad/mapping/strategy/step_strategy.py @@ -178,9 +178,10 @@ def get_data(self, x: v.StateVariable, y: v.StateVariable, global_x = False, glo The state variable to be used for the x-axis. y : v.StateVariable The state variable to be used for the y-axis. - x_is_global : bool, optional - Indicates whether `x` is a global or phase-local variable. - For example, if plotting global composition (`x`) vs. phase fraction (`y`), then `x` is global. + global_x : bool + Whether variable x applies to the global system + global_y : bool + Whether variable y applies to the global system set_nan_to_zero : bool, optional If True, NaN values will be set to zero in the data. diff --git a/pycalphad/mapping/strategy/strategy_base.py b/pycalphad/mapping/strategy/strategy_base.py index b417f6863..611e4eba4 100644 --- a/pycalphad/mapping/strategy/strategy_base.py +++ b/pycalphad/mapping/strategy/strategy_base.py @@ -176,16 +176,16 @@ def add_nodes_from_conditions(self, conditions: dict[v.StateVariable, float], di """ point = point_from_equilibrium(self.dbf, self.components, self.phases, conditions, models=self.models, phase_record_factory=self.phase_records) if point is None: - _log.warning(f"Point could not be found from {conditions}") + _log.info(f"Point could not be found from {conditions}") return False exit_hint, direction, err_reason = self._validate_custom_starting_point(point, direction) if err_reason is not None: - _log.warning(f"Point could not be added at {conditions}. {err_reason}") + _log.info(f"Point could not be added at {conditions}. {err_reason}") return False if exit_hint == ExitHint.NORMAL: - self.node_queue.add_node(self._create_node_from_point(point, None, None, None, exit_hint)) + self.node_queue.add_node(self._create_node_from_point(point, None, None, None, exit_hint), force_add) else: if direction is None: _log.info(f"No direction is given, adding point from {conditions} with both directions") diff --git a/pycalphad/tests/test_mapping_strategy.py b/pycalphad/tests/test_mapping_strategy.py index c3d596a42..601cc2c3f 100644 --- a/pycalphad/tests/test_mapping_strategy.py +++ b/pycalphad/tests/test_mapping_strategy.py @@ -14,6 +14,7 @@ from pycalphad.mapping.starting_points import point_from_equilibrium from pycalphad.mapping.zpf_equilibrium import find_global_min_point from pycalphad.mapping.primitives import Point, Node, Direction, ZPFLine, ZPFState +from pycalphad.mapping.plotting import get_label import pycalphad.tests.databases @@ -33,7 +34,7 @@ def test_binary_strategy(load_database): dbf = load_database() - ax, strategy = binplot(dbf, ["CR", "NI", "VA"], None, conditions={v.T: (1500, 2500, 40), v.X("CR"): (0, 1, 0.02), v.P: 101325}, return_strategy=True) + ax, strategy = binplot(dbf, ["CR", "NI", "VA"], None, conditions={v.T: (1500, 1800, 40), v.X("CR"): (0, 1, 0.02), v.P: 101325}, return_strategy=True) # Two-phase regions intended to show up in the Cr-Ni system desired_zpf_sets = [{"BCC_B2", "L12_FCC"}, {"BCC_B2", "LIQUID"}, {"L12_FCC", "LIQUID"}] @@ -52,11 +53,20 @@ def test_binary_strategy(load_database): for dnz in desired_node_sets: assert dnz in node_sets + num_nodes = len(strategy.node_queue.nodes) + strategy.add_nodes_from_conditions({v.T: 1600, v.P: 101325, v.X('CR'): 0.3}) + new_num_nodes = len(strategy.node_queue.nodes) + assert new_num_nodes == num_nodes + + strategy.add_nodes_from_conditions({v.T: 1600, v.P: 101325, v.X('CR'): 0.6}) + new_num_nodes = len(strategy.node_queue.nodes) + assert new_num_nodes == num_nodes + 2 + @select_database("crtiv_ghosh.tdb") def test_ternary_strategy(load_database): dbf = load_database() - ax, strategy = ternplot(dbf, ["CR", "TI", "V", "VA"], None, conds={v.X("CR"): (0, 1, 0.02), v.X("TI"): (0, 1, 0.02), v.T: 923, v.P: 101325}, return_strategy=True) + ax, strategy = ternplot(dbf, ["CR", "TI", "V", "VA"], None, conds={v.X("CR"): (0, 1, 0.02), v.X("TI"): (0, 1, 0.02), v.T: 923, v.P: 101325}, return_strategy=True, label_nodes=True) # Two-phase regions intended to show up in the Cr--Ti-V system desired_zpf_sets = [{"BCC_A2", "LAVES_C15"}, {"BCC_A2", "HCP_A3"}, {"HCP_A3", "LAVES_C15"}] @@ -75,12 +85,26 @@ def test_ternary_strategy(load_database): for dnz in desired_node_sets: assert dnz in node_sets + num_nodes = len(strategy.node_queue.nodes) + strategy.add_nodes_from_conditions({v.T: 923, v.P: 101325, v.X('CR'): 0.2, v.X('TI'): 0.2}) + new_num_nodes = len(strategy.node_queue.nodes) + assert new_num_nodes == num_nodes + + strategy.add_nodes_from_conditions({v.T: 923, v.P: 101325, v.X('CR'): 0.4, v.X('TI'): 0.4}) + new_num_nodes = len(strategy.node_queue.nodes) + assert new_num_nodes == num_nodes + 2 + + num_nodes = len(strategy.node_queue.nodes) + strategy.add_nodes_from_conditions({v.T: 923, v.P: 101325, v.X('CR'): 0.129, v.X('TI'): 0.861}, force_add=True) + new_num_nodes = len(strategy.node_queue.nodes) + assert new_num_nodes == num_nodes + 1 + @select_database("alcocrni.tdb") def test_step_strategy_through_single_phase(load_database): dbf = load_database() # Step strategy through single phase regions - strategy = StepStrategy(dbf, ["CR", "NI", "VA"], None, conditions={v.T: (1200, 2200, 10), v.X("CR"): 0.8, v.P: 101325}) + strategy = StepStrategy(dbf, ["CR", "NI", "VA"], None, conditions={v.T: (1300, 2000, 10), v.X("CR"): 0.8, v.P: 101325}) strategy.initialize() strategy.do_map() @@ -103,12 +127,34 @@ def test_step_strategy_through_single_phase(load_database): for dnz in desired_node_sets: assert dnz in node_sets + # Test behavior of data outputs + + # For T vs. CPM, x and y refers to properties of the entire system -> phases = ['SYSTEM'] + data = strategy.get_data(v.T, 'CPM') + assert len(data['data']) == 1 and 'SYSTEM' in data['data'] + + # v.X('CR') has phase wildcard '*' implicitly added, so phases = ['BCC_B2', 'L12_FCC', 'LIQUID'] + data = strategy.get_data(v.T, v.X('CR')) + assert len(set(list(data['data'].keys())).symmetric_difference({'BCC_B2', 'L12_FCC', 'LIQUID'})) == 0 + + # We force y to be global and x is already global, so phases = ['SYSTEM'] + data = strategy.get_data(v.T, v.X('CR'), global_y=True) + assert len(data['data']) == 1 and 'SYSTEM' in data['data'] + + # x is phase specific, so both x and y are global -> phases = ['SYSTEM'] + data = strategy.get_data(v.X('BCC_B2', 'CR'), v.T) + assert len(data['data']) == 1 and 'SYSTEM' in data['data'] + + # We force x to be global -> phases = ['SYSTEM'] + data = strategy.get_data(v.X('CR'), v.T, global_x=True) + assert len(data['data']) == 1 and 'SYSTEM' in data['data'] + @select_database("pbsn.tdb") def test_step_strategy_through_node(load_database): dbf = load_database() # Step strategy through single phase regions - strategy = StepStrategy(dbf, ["PB", "SN", "VA"], None, conditions={v.T: (373, 623, 5), v.X("SN"): 0.5, v.P: 101325}) + strategy = StepStrategy(dbf, ["PB", "SN", "VA"], None, conditions={v.T: (425, 550, 5), v.X("SN"): 0.5, v.P: 101325}) strategy.initialize() strategy.do_map() @@ -391,3 +437,17 @@ def test_ternary_strategy_process_metastable_node(load_database): assert len(strategy.node_queue.nodes) == 1+num_nodes assert strategy.node_queue.nodes[-1] == stable_node +def test_plot_labels(): + assert get_label(v.NP('*')) == 'Phase Fraction' + assert get_label(v.NP('BCC_A2')) == 'Phase Fraction (BCC_A2)' + + assert get_label(v.X('CR')) == 'X(Cr)' + assert get_label(v.X('BCC_A2', 'CR')) == 'X(BCC_A2, Cr)' + + assert get_label(v.W('CR')) == 'W(Cr)' + assert get_label(v.W('BCC_A2', 'CR')) == 'W(BCC_A2, Cr)' + + assert get_label(v.MU('CR')) == 'MU(Cr)' + assert get_label('CPM') == 'CPM' + assert get_label(v.T) == 'Temperature' + diff --git a/pycalphad/tests/test_variables.py b/pycalphad/tests/test_variables.py index 11971245a..1e8d38a0c 100644 --- a/pycalphad/tests/test_variables.py +++ b/pycalphad/tests/test_variables.py @@ -1,7 +1,7 @@ """ Test variables module. """ - +import copy import numpy as np from pycalphad import variables as v from pycalphad.tests.fixtures import select_database, load_database @@ -61,3 +61,23 @@ def test_component_and_species_repr_str_methods(): sp = v.Species(None) assert repr(sp) == "Species(None)" assert str(sp) == "" + +def test_deepcopy(): + ''' + Tests that deepcopy of variables produce the same variables + This addresses an unreported issue where copying the chemical potential + would use the name attribute rather than the species (this resulted in deepcopy(v.MU('A')) -> v.MU(v.MU('A'))) + ''' + assert copy.deepcopy(v.NP('*')) == v.NP('*') + assert copy.deepcopy(v.NP('A')) == v.NP('A') + + assert copy.deepcopy(v.X('B')) == v.X('B') + assert copy.deepcopy(v.X('A','B')) == v.X('A','B') + + assert copy.deepcopy(v.W('B')) == v.W('B') + assert copy.deepcopy(v.W('A','B')) == v.W('A','B') + + assert copy.deepcopy(v.Y('A',0,'B')) == v.Y('A',0,'B') + assert copy.deepcopy(v.T) == v.T + assert copy.deepcopy(v.P) == v.P + assert copy.deepcopy(v.MU('A')) == v.MU('A') diff --git a/pycalphad/variables.py b/pycalphad/variables.py index 5fa976dfe..a9408fcc2 100644 --- a/pycalphad/variables.py +++ b/pycalphad/variables.py @@ -482,6 +482,9 @@ def jansson_derivative(self, compsets, cur_conds, chemical_potentials, deltas: J def expand_wildcard(self, phase_names): return [self.__class__(phase_name) for phase_name in phase_names] + + def __reduce__(self): + return self.__class__, (self.phase_name,) def _latex(self, printer=None): "LaTeX representation."