From 3370f4a99b2337ae06ba1671f6712c403e370c76 Mon Sep 17 00:00:00 2001 From: Doug A Date: Mon, 19 Feb 2024 13:22:55 -0500 Subject: [PATCH 1/8] fix calculate derivative --- idaes/core/solvers/petsc.py | 88 ++++++++++---- idaes/core/solvers/tests/test_petsc.py | 154 +++++++++++++++++++++++++ 2 files changed, 222 insertions(+), 20 deletions(-) diff --git a/idaes/core/solvers/petsc.py b/idaes/core/solvers/petsc.py index 49b407ad3e..171bd1e685 100644 --- a/idaes/core/solvers/petsc.py +++ b/idaes/core/solvers/petsc.py @@ -29,7 +29,7 @@ from pyomo.core.expr.visitor import identify_variables import pyomo.dae as pyodae from pyomo.common import Executable -from pyomo.dae.flatten import flatten_dae_components +from pyomo.dae.flatten import flatten_dae_components, slice_component_along_sets from pyomo.util.subsystems import ( TemporarySubsystemManager, create_subsystem_block, @@ -428,7 +428,7 @@ def petsc_dae_by_time_element( symbolic_solver_labels=True, between=None, interpolate=True, - calculate_derivatives=True, + calculate_derivatives=False, previous_trajectory=None, representative_time=None, snes_options=None, @@ -711,17 +711,17 @@ def petsc_dae_by_time_element( # May not have trajectory from fixed variables and they # shouldn't change anyway, so only set not fixed vars var[t].value = vec[i] - if calculate_derivatives: - # the petsc solver interface does not currently return time - # derivatives, and if it did, they would be estimated based on a - # smaller time step. This option uses Pyomo.DAE's discretization - # equations to calculate the time derivative values - calculate_time_derivatives(m, time) - # return the solver results and trajectory if available + if calculate_derivatives: + # the petsc solver interface does not currently return time + # derivatives, and if it did, they would be estimated based on a + # smaller time step. This option uses Pyomo.DAE's discretization + # equations to calculate the time derivative values + calculate_time_derivatives(m, time, between=between) + # return the solver results and trajectory if available return PetscDAEResults(results=res_list, trajectory=tj) -def calculate_time_derivatives(m, time): +def calculate_time_derivatives(m, time, between=None): """Calculate the derivative values from the discretization equations. Args: @@ -731,19 +731,67 @@ def calculate_time_derivatives(m, time): Returns: None """ + # Leave between an optional argument for backwards compatibility + if between is None: + between=time for var in m.component_objects(pyo.Var): if isinstance(var, pyodae.DerivativeVar): if time in ComponentSet(var.get_continuousset_list()): - parent = var.parent_block() - name = var.local_name + "_disc_eq" - disc_eq = getattr(parent, name) - for i, v in var.items(): - try: - if disc_eq[i].active: - v.value = 0 # Make sure there is a value - calculate_variable_from_constraint(v, disc_eq[i]) - except KeyError: - pass # discretization equation may not exist at first time + parent_block = var.parent_block() + disc_eq = getattr(parent_block, var.local_name + "_disc_eq") + + deriv_dict = dict( + (key, pyo.Reference(slc)) + for key, slc in slice_component_along_sets(var, (time,)) + ) + disc_dict = dict( + (key, pyo.Reference(slc)) + for key, slc in slice_component_along_sets(disc_eq, (time,)) + ) + + for key, deriv in deriv_dict.items(): + # state = state_dict[key] + disc_eq = disc_dict[key] + for t in time: + if t < between.first() or t > between.last(): + # Outside of integration range, skip calculation + continue + # Check time index to decide what constraints to scale + if t == time.first() or t == time.last(): + try: + if disc_eq[t].active and not deriv[t].fixed: + old_value = deriv[t].value + deriv[t].value = 0 # Make sure there is a value + calculate_variable_from_constraint(deriv[t], disc_eq[t]) + except KeyError: + # Discretization and continuity equations may or may not exist at the first or last time + # points depending on the method. Backwards skips first, forwards skips last, central skips + # both (which means the user needs to provide additional equations) + pass + + elif t == between.first() or t == between.last(): + # At edges of between, it's unclear which adjacent + # values of state variables have been populated. + # Therefore we might get hit with value errors. + + # TODO This calculates the value of the derivative even + # if one of the state var values is from outside the + # integration range, so long as it's initialized. Is + # this the desired behavior? + try: + if disc_eq[t].active and not deriv[t].fixed: + old_value = deriv[t].value + deriv[t].value = 0 # Make sure there is a value + calculate_variable_from_constraint(deriv[t], disc_eq[t]) + except ValueError: + # Reset deriv value to old value + if disc_eq[t].active and not deriv[t].fixed: + deriv[t].value = old_value + + else: + if disc_eq[t].active and not deriv[t].fixed: + deriv[t].value = 0 # Make sure there is a value + calculate_variable_from_constraint(deriv[t], disc_eq[t]) class PetscTrajectory(object): diff --git a/idaes/core/solvers/tests/test_petsc.py b/idaes/core/solvers/tests/test_petsc.py index 8bf8a0eb53..efa90d85ea 100644 --- a/idaes/core/solvers/tests/test_petsc.py +++ b/idaes/core/solvers/tests/test_petsc.py @@ -988,3 +988,157 @@ def diff_eq_rule(m, t): petsc.petsc_dae_by_time_element( m, time=m.time, between=[0.0, 10.0], representative_time=5.0 ) + +@pytest.mark.unit +@pytest.mark.skipif(not petsc.petsc_available(), reason="PETSc solver not available") +def test_calculate_derivatives(): + m = pyo.ConcreteModel() + + m.time = pyodae.ContinuousSet(initialize=(0.0, 1.0, 2.0)) + m.x = pyo.Var(m.time) + m.u = pyo.Var(m.time) + m.dxdt = pyodae.DerivativeVar(m.x, wrt=m.time) + + def diff_eq_rule(m, t): + return m.dxdt[t] == m.x[t] ** 2 - m.u[t] + + m.diff_eq = pyo.Constraint(m.time, rule=diff_eq_rule) + + discretizer = pyo.TransformationFactory("dae.finite_difference") + discretizer.apply_to(m, nfe=1, scheme="BACKWARD") + + m.u.fix(1.0) + m.x[0].fix(0.0) + m.diff_eq[0].deactivate() + + res = petsc.petsc_dae_by_time_element( + m, + time=m.time, + between=[0.0, 2.0], + ts_options={ + "--ts_type": "beuler", + "--ts_dt": 3e-2, + }, + skip_initial=True, # With u and x fixed, no variables to solve for at t0 + calculate_derivatives=True + ) + # No value assigned at 0 because there's no corresponding discretization equation + assert m.dxdt[0].value is None + # It should backfill values for interpolated points like t=1 + assert pyo.value(m.dxdt[1]) == pytest.approx(-0.7580125427537873, rel=1e-3) + assert pyo.value(m.dxdt[2]) == pytest.approx(-0.2034733131369807, rel=1e-3) + +@pytest.mark.unit +@pytest.mark.skipif(not petsc.petsc_available(), reason="PETSc solver not available") +def test_calculate_derivatives_integrate_first_half(): + m = pyo.ConcreteModel() + + m.time = pyodae.ContinuousSet(initialize=(0.0, 1.0, 2.0)) + m.x = pyo.Var(m.time) + m.u = pyo.Var(m.time) + m.dxdt = pyodae.DerivativeVar(m.x, wrt=m.time) + + def diff_eq_rule(m, t): + return m.dxdt[t] == m.x[t] ** 2 - m.u[t] + + m.diff_eq = pyo.Constraint(m.time, rule=diff_eq_rule) + + discretizer = pyo.TransformationFactory("dae.finite_difference") + discretizer.apply_to(m, nfe=1, scheme="BACKWARD") + + m.u.fix(1.0) + m.x[0].fix(0.0) + m.diff_eq[0].deactivate() + + res = petsc.petsc_dae_by_time_element( + m, + time=m.time, + between=[0.0, 1.0], + ts_options={ + "--ts_type": "beuler", + "--ts_dt": 3e-2, + }, + skip_initial=True, # With u and x fixed, no variables to solve for at t0 + calculate_derivatives=True + ) + # No value assigned at 0 because there's no corresponding discretization equation + assert m.dxdt[0].value is None + assert pyo.value(m.dxdt[1]) == pytest.approx(-0.7580125427537873, rel=1e-3) + assert m.dxdt[2].value is None + +@pytest.mark.unit +@pytest.mark.skipif(not petsc.petsc_available(), reason="PETSc solver not available") +def test_calculate_derivatives_integrate_second_half(): + m = pyo.ConcreteModel() + + m.time = pyodae.ContinuousSet(initialize=(0.0, 1.0, 2.0)) + m.x = pyo.Var(m.time) + m.u = pyo.Var(m.time) + m.dxdt = pyodae.DerivativeVar(m.x, wrt=m.time) + + def diff_eq_rule(m, t): + return m.dxdt[t] == m.x[t] ** 2 - m.u[t] + + m.diff_eq = pyo.Constraint(m.time, rule=diff_eq_rule) + + discretizer = pyo.TransformationFactory("dae.finite_difference") + discretizer.apply_to(m, nfe=1, scheme="BACKWARD") + + m.u.fix(1.0) + m.x[1].fix(-0.7580125427537873) + m.diff_eq[0].deactivate() + + res = petsc.petsc_dae_by_time_element( + m, + time=m.time, + between=[1.0, 2.0], + ts_options={ + "--ts_type": "beuler", + "--ts_dt": 3e-2, + }, + skip_initial=True, # With u and x fixed, no variables to solve for at t0 + calculate_derivatives=True + ) + # No value assigned at 0 because there's no corresponding discretization equation + assert m.dxdt[0].value is None + assert m.dxdt[1].value is None + assert pyo.value(m.dxdt[2]) == pytest.approx(-0.2034733131369807, rel=1e-3) + +@pytest.mark.unit +@pytest.mark.skipif(not petsc.petsc_available(), reason="PETSc solver not available") +def test_calculate_derivatives_forward_difference(): + m = pyo.ConcreteModel() + + m.time = pyodae.ContinuousSet(initialize=(0.0, 1.0, 2.0)) + m.x = pyo.Var(m.time) + m.u = pyo.Var(m.time) + m.dxdt = pyodae.DerivativeVar(m.x, wrt=m.time) + + def diff_eq_rule(m, t): + return m.dxdt[t] == m.x[t] ** 2 - m.u[t] + + m.diff_eq = pyo.Constraint(m.time, rule=diff_eq_rule) + + discretizer = pyo.TransformationFactory("dae.finite_difference") + discretizer.apply_to(m, nfe=1, scheme="FORWARD") + + m.u.fix(1.0) + m.x[0].fix(0.0) + + res = petsc.petsc_dae_by_time_element( + m, + time=m.time, + between=[0.0, 2.0], + ts_options={ + "--ts_type": "beuler", + "--ts_dt": 3e-2, + }, + skip_initial=True, # With u and x fixed, no variables to solve for at t0 + calculate_derivatives=True + ) + # It should backfill values for interpolated points like t=1 + assert pyo.value(m.dxdt[0]) == pytest.approx(-0.7580125427537873, rel=1e-3) + assert pyo.value(m.dxdt[1]) == pytest.approx(-0.2034733131369807, rel=1e-3) + # No value assigned at 2 because there's no corresponding discretization equation + # TODO I don't understand how a value is getting assigned here + # assert m.dxdt[2].value is None From 731783a2d9d8e26b3cdbe30d38b79d45c341032a Mon Sep 17 00:00:00 2001 From: Doug A Date: Mon, 19 Feb 2024 13:25:22 -0500 Subject: [PATCH 2/8] run Black --- idaes/core/solvers/petsc.py | 14 +++++++++----- idaes/core/solvers/tests/test_petsc.py | 20 ++++++++++++-------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/idaes/core/solvers/petsc.py b/idaes/core/solvers/petsc.py index 171bd1e685..9589534031 100644 --- a/idaes/core/solvers/petsc.py +++ b/idaes/core/solvers/petsc.py @@ -733,7 +733,7 @@ def calculate_time_derivatives(m, time, between=None): """ # Leave between an optional argument for backwards compatibility if between is None: - between=time + between = time for var in m.component_objects(pyo.Var): if isinstance(var, pyodae.DerivativeVar): if time in ComponentSet(var.get_continuousset_list()): @@ -760,9 +760,11 @@ def calculate_time_derivatives(m, time, between=None): if t == time.first() or t == time.last(): try: if disc_eq[t].active and not deriv[t].fixed: - old_value = deriv[t].value + old_value = deriv[t].value deriv[t].value = 0 # Make sure there is a value - calculate_variable_from_constraint(deriv[t], disc_eq[t]) + calculate_variable_from_constraint( + deriv[t], disc_eq[t] + ) except KeyError: # Discretization and continuity equations may or may not exist at the first or last time # points depending on the method. Backwards skips first, forwards skips last, central skips @@ -780,9 +782,11 @@ def calculate_time_derivatives(m, time, between=None): # this the desired behavior? try: if disc_eq[t].active and not deriv[t].fixed: - old_value = deriv[t].value + old_value = deriv[t].value deriv[t].value = 0 # Make sure there is a value - calculate_variable_from_constraint(deriv[t], disc_eq[t]) + calculate_variable_from_constraint( + deriv[t], disc_eq[t] + ) except ValueError: # Reset deriv value to old value if disc_eq[t].active and not deriv[t].fixed: diff --git a/idaes/core/solvers/tests/test_petsc.py b/idaes/core/solvers/tests/test_petsc.py index efa90d85ea..4e51a5c3df 100644 --- a/idaes/core/solvers/tests/test_petsc.py +++ b/idaes/core/solvers/tests/test_petsc.py @@ -989,6 +989,7 @@ def diff_eq_rule(m, t): m, time=m.time, between=[0.0, 10.0], representative_time=5.0 ) + @pytest.mark.unit @pytest.mark.skipif(not petsc.petsc_available(), reason="PETSc solver not available") def test_calculate_derivatives(): @@ -1019,8 +1020,8 @@ def diff_eq_rule(m, t): "--ts_type": "beuler", "--ts_dt": 3e-2, }, - skip_initial=True, # With u and x fixed, no variables to solve for at t0 - calculate_derivatives=True + skip_initial=True, # With u and x fixed, no variables to solve for at t0 + calculate_derivatives=True, ) # No value assigned at 0 because there's no corresponding discretization equation assert m.dxdt[0].value is None @@ -1028,6 +1029,7 @@ def diff_eq_rule(m, t): assert pyo.value(m.dxdt[1]) == pytest.approx(-0.7580125427537873, rel=1e-3) assert pyo.value(m.dxdt[2]) == pytest.approx(-0.2034733131369807, rel=1e-3) + @pytest.mark.unit @pytest.mark.skipif(not petsc.petsc_available(), reason="PETSc solver not available") def test_calculate_derivatives_integrate_first_half(): @@ -1058,14 +1060,15 @@ def diff_eq_rule(m, t): "--ts_type": "beuler", "--ts_dt": 3e-2, }, - skip_initial=True, # With u and x fixed, no variables to solve for at t0 - calculate_derivatives=True + skip_initial=True, # With u and x fixed, no variables to solve for at t0 + calculate_derivatives=True, ) # No value assigned at 0 because there's no corresponding discretization equation assert m.dxdt[0].value is None assert pyo.value(m.dxdt[1]) == pytest.approx(-0.7580125427537873, rel=1e-3) assert m.dxdt[2].value is None + @pytest.mark.unit @pytest.mark.skipif(not petsc.petsc_available(), reason="PETSc solver not available") def test_calculate_derivatives_integrate_second_half(): @@ -1096,14 +1099,15 @@ def diff_eq_rule(m, t): "--ts_type": "beuler", "--ts_dt": 3e-2, }, - skip_initial=True, # With u and x fixed, no variables to solve for at t0 - calculate_derivatives=True + skip_initial=True, # With u and x fixed, no variables to solve for at t0 + calculate_derivatives=True, ) # No value assigned at 0 because there's no corresponding discretization equation assert m.dxdt[0].value is None assert m.dxdt[1].value is None assert pyo.value(m.dxdt[2]) == pytest.approx(-0.2034733131369807, rel=1e-3) + @pytest.mark.unit @pytest.mark.skipif(not petsc.petsc_available(), reason="PETSc solver not available") def test_calculate_derivatives_forward_difference(): @@ -1133,8 +1137,8 @@ def diff_eq_rule(m, t): "--ts_type": "beuler", "--ts_dt": 3e-2, }, - skip_initial=True, # With u and x fixed, no variables to solve for at t0 - calculate_derivatives=True + skip_initial=True, # With u and x fixed, no variables to solve for at t0 + calculate_derivatives=True, ) # It should backfill values for interpolated points like t=1 assert pyo.value(m.dxdt[0]) == pytest.approx(-0.7580125427537873, rel=1e-3) From 6e9148aabc9756a6077e1a707a06280081348554 Mon Sep 17 00:00:00 2001 From: Doug A Date: Mon, 19 Feb 2024 14:10:34 -0500 Subject: [PATCH 3/8] test central difference scheme --- idaes/core/solvers/tests/test_petsc.py | 130 ++++++++++++++++++++++++- 1 file changed, 125 insertions(+), 5 deletions(-) diff --git a/idaes/core/solvers/tests/test_petsc.py b/idaes/core/solvers/tests/test_petsc.py index 4e51a5c3df..6da3516cd6 100644 --- a/idaes/core/solvers/tests/test_petsc.py +++ b/idaes/core/solvers/tests/test_petsc.py @@ -982,7 +982,7 @@ def diff_eq_rule(m, t): m.diff_eq = pyo.Constraint(m.time, rule=diff_eq_rule) discretizer = pyo.TransformationFactory("dae.finite_difference") - discretizer.apply_to(m, nfe=1, scheme="BACKWARD") + discretizer.apply_to(m, nfe=2, scheme="BACKWARD") with pytest.raises(RuntimeError, match="representative_time"): petsc.petsc_dae_by_time_element( @@ -1006,7 +1006,7 @@ def diff_eq_rule(m, t): m.diff_eq = pyo.Constraint(m.time, rule=diff_eq_rule) discretizer = pyo.TransformationFactory("dae.finite_difference") - discretizer.apply_to(m, nfe=1, scheme="BACKWARD") + discretizer.apply_to(m, nfe=2, scheme="BACKWARD") m.u.fix(1.0) m.x[0].fix(0.0) @@ -1046,7 +1046,7 @@ def diff_eq_rule(m, t): m.diff_eq = pyo.Constraint(m.time, rule=diff_eq_rule) discretizer = pyo.TransformationFactory("dae.finite_difference") - discretizer.apply_to(m, nfe=1, scheme="BACKWARD") + discretizer.apply_to(m, nfe=2, scheme="BACKWARD") m.u.fix(1.0) m.x[0].fix(0.0) @@ -1085,7 +1085,7 @@ def diff_eq_rule(m, t): m.diff_eq = pyo.Constraint(m.time, rule=diff_eq_rule) discretizer = pyo.TransformationFactory("dae.finite_difference") - discretizer.apply_to(m, nfe=1, scheme="BACKWARD") + discretizer.apply_to(m, nfe=2, scheme="BACKWARD") m.u.fix(1.0) m.x[1].fix(-0.7580125427537873) @@ -1124,7 +1124,7 @@ def diff_eq_rule(m, t): m.diff_eq = pyo.Constraint(m.time, rule=diff_eq_rule) discretizer = pyo.TransformationFactory("dae.finite_difference") - discretizer.apply_to(m, nfe=1, scheme="FORWARD") + discretizer.apply_to(m, nfe=2, scheme="FORWARD") m.u.fix(1.0) m.x[0].fix(0.0) @@ -1146,3 +1146,123 @@ def diff_eq_rule(m, t): # No value assigned at 2 because there's no corresponding discretization equation # TODO I don't understand how a value is getting assigned here # assert m.dxdt[2].value is None + + +@pytest.mark.unit +@pytest.mark.skipif(not petsc.petsc_available(), reason="PETSc solver not available") +def test_calculate_derivatives_central_difference(): + m = pyo.ConcreteModel() + + m.time = pyodae.ContinuousSet(initialize=(0.0, 1.0, 2.0)) + m.x = pyo.Var(m.time) + m.u = pyo.Var(m.time) + m.dxdt = pyodae.DerivativeVar(m.x, wrt=m.time) + + def diff_eq_rule(m, t): + return m.dxdt[t] == m.x[t] ** 2 - m.u[t] + + m.diff_eq = pyo.Constraint(m.time, rule=diff_eq_rule) + + discretizer = pyo.TransformationFactory("dae.finite_difference") + discretizer.apply_to(m, nfe=2, scheme="CENTRAL") + + m.u.fix(1.0) + m.x[0].fix(0.0) + + res = petsc.petsc_dae_by_time_element( + m, + time=m.time, + between=[0.0, 2.0], + ts_options={ + "--ts_type": "beuler", + "--ts_dt": 3e-2, + }, + skip_initial=True, # With u and x fixed, no variables to solve for at t0 + calculate_derivatives=True, + ) + + assert m.dxdt[0].value is None + # It should backfill values for interpolated points like t=1 + assert pyo.value(m.dxdt[1]) == pytest.approx(-0.480742927945384, rel=1e-3) + # No value assigned at 2 because there's no corresponding discretization equation + # TODO I don't understand how a value is getting assigned here + # assert m.dxdt[2].value is None + + +@pytest.mark.unit +@pytest.mark.skipif(not petsc.petsc_available(), reason="PETSc solver not available") +def test_calculate_derivatives_collocation_lr(): + m = pyo.ConcreteModel() + + m.time = pyodae.ContinuousSet(initialize=(0.0, 1.0, 2.0)) + m.x = pyo.Var(m.time) + m.u = pyo.Var(m.time) + m.dxdt = pyodae.DerivativeVar(m.x, wrt=m.time) + + def diff_eq_rule(m, t): + return m.dxdt[t] == m.x[t] ** 2 - m.u[t] + + m.diff_eq = pyo.Constraint(m.time, rule=diff_eq_rule) + + discretizer = pyo.TransformationFactory("dae.collocation") + discretizer.apply_to(m, nfe=2, scheme="LAGRANGE-RADAU") + + m.u.fix(1.0) + m.x[0].fix(0.0) + + res = petsc.petsc_dae_by_time_element( + m, + time=m.time, + between=[0.0, 2.0], + ts_options={ + "--ts_type": "beuler", + "--ts_dt": 3e-2, + }, + skip_initial=True, # With u and x fixed, no variables to solve for at t0 + calculate_derivatives=True, + ) + + assert m.dxdt[0].value is None + # It should backfill values for interpolated points like t=1 + assert pyo.value(m.dxdt[1]) == pytest.approx(-0.3787483909827891, rel=1e-3) + assert pyo.value(m.dxdt[2]) == pytest.approx(-0.07952012649572815, rel=1e-3) + + +# This test fails because the PETSc interface doesn't work with Lagrange-Legendre collocation +# @pytest.mark.unit +# @pytest.mark.skipif(not petsc.petsc_available(), reason="PETSc solver not available") +# def test_calculate_derivatives_collocation_ll(): +# m = pyo.ConcreteModel() + +# m.time = pyodae.ContinuousSet(initialize=(0.0, 1.0, 2.0)) +# m.x = pyo.Var(m.time) +# m.u = pyo.Var(m.time) +# m.dxdt = pyodae.DerivativeVar(m.x, wrt=m.time) + +# def diff_eq_rule(m, t): +# return m.dxdt[t] == m.x[t] ** 2 - m.u[t] + +# m.diff_eq = pyo.Constraint(m.time, rule=diff_eq_rule) + +# discretizer = pyo.TransformationFactory("dae.collocation") +# discretizer.apply_to(m, nfe=2, scheme="LAGRANGE-LEGENDRE") + +# m.u.fix(1.0) +# m.x[0].fix(0.0) + +# res = petsc.petsc_dae_by_time_element( +# m, +# time=m.time, +# between=[0.0, 2.0], +# ts_options={ +# "--ts_type": "beuler", +# "--ts_dt": 3e-2, +# }, +# skip_initial=True, # With u and x fixed, no variables to solve for at t0 +# calculate_derivatives=True, +# ) + +# assert m.dxdt[0].value is None +# # It should backfill values for interpolated points like t=1 +# assert pyo.value(m.dxdt[1]) == pytest.approx(-0.3787483909827891, rel=1e-3) +# assert pyo.value(m.dxdt[2]) == pytest.approx(-0.07952012649572815, rel=1e-3) From 84982873f4e28287d3d99f82606c3e10080e0f04 Mon Sep 17 00:00:00 2001 From: Doug A Date: Fri, 23 Feb 2024 11:14:42 -0500 Subject: [PATCH 4/8] simplify conditionals --- idaes/core/solvers/petsc.py | 53 ++++++++++++++----------------------- 1 file changed, 20 insertions(+), 33 deletions(-) diff --git a/idaes/core/solvers/petsc.py b/idaes/core/solvers/petsc.py index 9589534031..b2a4071039 100644 --- a/idaes/core/solvers/petsc.py +++ b/idaes/core/solvers/petsc.py @@ -756,46 +756,33 @@ def calculate_time_derivatives(m, time, between=None): if t < between.first() or t > between.last(): # Outside of integration range, skip calculation continue - # Check time index to decide what constraints to scale - if t == time.first() or t == time.last(): - try: - if disc_eq[t].active and not deriv[t].fixed: - old_value = deriv[t].value - deriv[t].value = 0 # Make sure there is a value - calculate_variable_from_constraint( - deriv[t], disc_eq[t] - ) - except KeyError: - # Discretization and continuity equations may or may not exist at the first or last time - # points depending on the method. Backwards skips first, forwards skips last, central skips - # both (which means the user needs to provide additional equations) - pass - - elif t == between.first() or t == between.last(): - # At edges of between, it's unclear which adjacent - # values of state variables have been populated. - # Therefore we might get hit with value errors. - + try: # TODO This calculates the value of the derivative even # if one of the state var values is from outside the # integration range, so long as it's initialized. Is # this the desired behavior? - try: - if disc_eq[t].active and not deriv[t].fixed: - old_value = deriv[t].value - deriv[t].value = 0 # Make sure there is a value - calculate_variable_from_constraint( - deriv[t], disc_eq[t] - ) - except ValueError: - # Reset deriv value to old value - if disc_eq[t].active and not deriv[t].fixed: - deriv[t].value = old_value - - else: if disc_eq[t].active and not deriv[t].fixed: + old_value = deriv[t].value deriv[t].value = 0 # Make sure there is a value calculate_variable_from_constraint(deriv[t], disc_eq[t]) + except KeyError as err: + # Discretization and continuity equations may or may not exist at the first or last time + # points depending on the method. Backwards skips first, forwards skips last, central skips + # both (which means the user needs to provide additional equations) + if t == time.first() or t == time.last(): + pass + else: + raise err + except ValueError as err: + # At edges of between, it's unclear which adjacent + # values of state variables have been populated. + # Therefore we might get hit with value errors. + if t == between.first() or t == between.last(): + # Reset deriv value to old value + if disc_eq[t].active and not deriv[t].fixed: + deriv[t].value = old_value + else: + raise err class PetscTrajectory(object): From 2a749826ae7fd2181f8c40c0408d7b1b2a8863b5 Mon Sep 17 00:00:00 2001 From: Doug A Date: Fri, 23 Feb 2024 17:59:23 -0500 Subject: [PATCH 5/8] attempt to get around pylint error --- idaes/core/solvers/petsc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/idaes/core/solvers/petsc.py b/idaes/core/solvers/petsc.py index b2a4071039..40c363ebb8 100644 --- a/idaes/core/solvers/petsc.py +++ b/idaes/core/solvers/petsc.py @@ -761,8 +761,8 @@ def calculate_time_derivatives(m, time, between=None): # if one of the state var values is from outside the # integration range, so long as it's initialized. Is # this the desired behavior? + old_value = deriv[t].value if disc_eq[t].active and not deriv[t].fixed: - old_value = deriv[t].value deriv[t].value = 0 # Make sure there is a value calculate_variable_from_constraint(deriv[t], disc_eq[t]) except KeyError as err: From abfaf119a5258bb9d3180cd6964c57fc49f9ef70 Mon Sep 17 00:00:00 2001 From: Ludovico Bianchi Date: Wed, 28 Feb 2024 18:50:18 -0600 Subject: [PATCH 6/8] Disable Pylint false positive --- idaes/core/solvers/petsc.py | 1 + 1 file changed, 1 insertion(+) diff --git a/idaes/core/solvers/petsc.py b/idaes/core/solvers/petsc.py index 40c363ebb8..e7ad5cf1a9 100644 --- a/idaes/core/solvers/petsc.py +++ b/idaes/core/solvers/petsc.py @@ -780,6 +780,7 @@ def calculate_time_derivatives(m, time, between=None): if t == between.first() or t == between.last(): # Reset deriv value to old value if disc_eq[t].active and not deriv[t].fixed: + # pylint: disable-next=used-before-assignment deriv[t].value = old_value else: raise err From 4b589ce454c3dca97d50a80f1d8de959b46d205e Mon Sep 17 00:00:00 2001 From: Doug A Date: Thu, 29 Feb 2024 10:49:21 -0500 Subject: [PATCH 7/8] pull outside for fix --- idaes/core/solvers/petsc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/idaes/core/solvers/petsc.py b/idaes/core/solvers/petsc.py index 40c363ebb8..20252891e4 100644 --- a/idaes/core/solvers/petsc.py +++ b/idaes/core/solvers/petsc.py @@ -756,12 +756,12 @@ def calculate_time_derivatives(m, time, between=None): if t < between.first() or t > between.last(): # Outside of integration range, skip calculation continue + old_value = deriv[t].value try: # TODO This calculates the value of the derivative even # if one of the state var values is from outside the # integration range, so long as it's initialized. Is # this the desired behavior? - old_value = deriv[t].value if disc_eq[t].active and not deriv[t].fixed: deriv[t].value = 0 # Make sure there is a value calculate_variable_from_constraint(deriv[t], disc_eq[t]) From 6decddaaaa0789f10820963af7f66da06b3b75f4 Mon Sep 17 00:00:00 2001 From: Doug A Date: Thu, 29 Feb 2024 13:21:09 -0500 Subject: [PATCH 8/8] remove redundant pylint check --- idaes/core/solvers/petsc.py | 1 - 1 file changed, 1 deletion(-) diff --git a/idaes/core/solvers/petsc.py b/idaes/core/solvers/petsc.py index 5ff76a589d..20252891e4 100644 --- a/idaes/core/solvers/petsc.py +++ b/idaes/core/solvers/petsc.py @@ -780,7 +780,6 @@ def calculate_time_derivatives(m, time, between=None): if t == between.first() or t == between.last(): # Reset deriv value to old value if disc_eq[t].active and not deriv[t].fixed: - # pylint: disable-next=used-before-assignment deriv[t].value = old_value else: raise err