def root_finder(): m = ConcreteModel() # Define external function methods m.proc_Z_liq = ExternalFunction(library=_so, function="ceos_z_liq") m.proc_Z_vap = ExternalFunction(library=_so, function="ceos_z_vap") m.proc_Z_liq_x = ExternalFunction(library=_so, function="ceos_z_liq_extend") m.proc_Z_vap_x = ExternalFunction(library=_so, function="ceos_z_vap_extend") return m
def setUp(self): self.m = ConcreteModel() self.m.z = Var(range(3), domain=Reals, initialize=2.) self.m.x = Var(range(2), initialize=2.) self.m.x[1] = 1.0 def blackbox(a, b): return sin(a - b) def grad_blackbox(args, fixed): a, b = args[:2] return [cos(a - b), -cos(a - b)] self.m.bb = ExternalFunction(blackbox, grad_blackbox) self.m.obj = Objective(expr=(self.m.z[0] - 1.0)**2 + (self.m.z[0] - self.m.z[1])**2 + (self.m.z[2] - 1.0)**2 + (self.m.x[0] - 1.0)**4 + (self.m.x[1] - 1.0)**6) self.m.c1 = Constraint(expr=(self.m.x[0] * self.m.z[0]**2 + self.m.bb(self.m.x[0], self.m.x[1]) == 2 * sqrt(2.0))) self.m.c2 = Constraint(expr=self.m.z[2]**4 * self.m.z[1]**2 + self.m.z[1] == 8 + sqrt(2.0)) self.config = _trf_config() self.ext_fcn_surrogate_map_rule = lambda comp, ef: 0 self.interface = TRFInterface(self.m, [self.m.z[0], self.m.z[1], self.m.z[2]], self.ext_fcn_surrogate_map_rule, self.config)
def delta_temperature_underwood_callback(b): r""" This is a callback for a temperature difference expression to calculate :math:`\Delta T` in the heat exchanger model using log-mean temperature difference (LMTD) approximation given by Underwood (1970). It can be supplied to "delta_temperature_callback" HeatExchanger configuration option. This uses a cube root function that works with negative numbers returning the real negative root. This should always evaluate successfully. This form is .. math:: \Delta T = \left(\frac{ \Delta T_1^\frac{1}{3} + \Delta T_2^\frac{1}{3}}{2}\right)^3 where :math:`\Delta T_1` is the temperature difference at the hot inlet end and :math:`\Delta T_2` is the temperature difference at the hot outlet end. """ dT1 = b.delta_temperature_in dT2 = b.delta_temperature_out temp_units = pyunits.get_units(dT1[dT1.index_set().first()]) # external function that ruturns the real root, for the cuberoot of negitive # numbers, so it will return without error for positive and negitive dT. b.cbrt = ExternalFunction(library=functions_lib(), function="cbrt", arg_units=[temp_units]) @b.Expression(b.flowsheet().time) def delta_temperature(b, t): return ((b.cbrt(dT1[t]) + b.cbrt(dT2[t])) / 2.0)**3 * temp_units
def setUp(self): self.m = ConcreteModel() self.m.z = Var(range(3), domain=Reals, initialize=2.) self.m.x = Var(range(2), initialize=2.) self.m.x[1] = 1.0 def blackbox(a, b): return sin(a - b) def grad_blackbox(args, fixed): a, b = args[:2] return [cos(a - b), -cos(a - b)] self.m.bb = ExternalFunction(blackbox, grad_blackbox) self.m.obj = Objective(expr=(self.m.z[0] - 1.0)**2 + (self.m.z[0] - self.m.z[1])**2 + (self.m.z[2] - 1.0)**2 + (self.m.x[0] - 1.0)**4 + (self.m.x[1] - 1.0)**6) self.m.c1 = Constraint(expr=(self.m.x[0] * self.m.z[0]**2 + self.m.bb(self.m.x[0], self.m.x[1]) == 2 * sqrt(2.0))) self.m.c2 = Constraint(expr=self.m.z[2]**4 * self.m.z[1]**2 + self.m.z[1] == 8 + sqrt(2.0)) self.decision_variables = [self.m.z[0], self.m.z[1], self.m.z[2]]
def setUp(self): # Borrowed this test model from the trust region tests m = ConcreteModel() m.z = Var(range(3), domain=Reals, initialize=2.) m.x = Var(range(4), initialize=2.) m.x[1] = 1.0 m.x[2] = 0.0 m.x[3] = None m.b1 = Block() m.b1.e1 = Expression(expr=m.x[0] + m.x[1]) m.b1.e2 = Expression(expr=m.x[0]/m.x[2]) m.b1.e3 = Expression(expr=m.x[3]*m.x[1]) m.b1.e4 = Expression(expr=log(m.x[2])) m.b1.e5 = Expression(expr=log(m.x[2] - 2)) def blackbox(a,b): return sin(a-b) self.bb = ExternalFunction(blackbox) m.obj = Objective( expr=(m.z[0]-1.0)**2 + (m.z[0]-m.z[1])**2 + (m.z[2]-1.0)**2 \ + (m.x[0]-1.0)**4 + (m.x[1]-1.0)**6 # + m.bb(m.x[0],m.x[1]) ) m.c1 = Constraint(expr=m.x[0] * m.z[0]**2 + self.bb(m.x[0],m.x[1]) == 2*sqrt(2.0)) m.c2 = Constraint(expr=m.z[2]**4 * m.z[1]**2 + m.z[1] == 8+sqrt(2.0)) m.c3 = Constraint(expr=m.x[1] == 3) m.c4 = Constraint(expr=0 == 3/m.x[2]) m.c5 = Constraint(expr=0 == log(m.x[2])) m.c6 = Constraint(expr=0 == log(m.x[2]-4)) m.c7 = Constraint(expr=0 == log(m.x[3])) m.p1 = Param(mutable=True, initialize=1) m.c8 = Constraint(expr = m.x[1] <= 1/m.p1) m.p1 = 0 self.m = m.clone()
def add_funcs(self, names=None): if names is None: names = [] elif isinstance(names, str): names = [names] for name in names: if hasattr(self.blk, name): continue setattr(self.blk, name, ExternalFunction(**self._external[name].kwargs()))
def test_solve_gsl_function(self): DLL = find_GSL() if not DLL: self.skipTest("Could not find the amplgsl.dll library") model = ConcreteModel() model.z_func = ExternalFunction(library=DLL, function="gsl_sf_gamma") model.x = Var(initialize=3, bounds=(1e-5, None)) model.o = Objective(expr=model.z_func(model.x)) nlp = PyomoNLP(model) self.assertAlmostEqual(nlp.evaluate_objective(), 2, 7) assert "AMPLFUNC" not in os.environ
def eq_lmtd(b, t): dT_in = b.delta_temperature_in dT_out = b.delta_temperature_out temp_units = pyunits.get_units(dT_in) dT_avg = (dT_in + dT_out) / 2 # external function that ruturns the real root, for the cuberoot of negitive # numbers, so it will return without error for positive and negitive dT. b.cbrt = ExternalFunction(library=functions_lib(), function="cbrt", arg_units=[temp_units**3]) return b.lmtd == b.cbrt((dT_in * dT_out * dT_avg)) * temp_units
def _external_model(self): DLL = find_GSL() if not DLL: self.skipTest("Could not find the amplgsl.dll library") m = ConcreteModel() m.hypot = ExternalFunction(library=DLL, function="gsl_hypot") m.p = Param(initialize=1, mutable=True) m.x = Var(initialize=3, bounds=(1e-5, None)) m.y = Var(initialize=3, bounds=(0, None)) m.z = Var(initialize=1) m.o = Objective(expr=m.z**2 * m.hypot(m.p * m.x, m.p + m.y)**2) self.assertAlmostEqual(value(m.o), 25.0, 7) return m
def test_identify_mutable_parameters_params(self): m = ConcreteModel() m.I = RangeSet(3) m.a = Param(initialize=1, mutable=True) m.b = Param(m.I, initialize=1, mutable=True) m.p = Var(initialize=1) m.x = ExternalFunction(library='foo.so', function='bar') # # Identify variables in various algebraic expressions # self.assertEqual(list(identify_mutable_parameters(m.a)), [m.a]) self.assertEqual(list(identify_mutable_parameters(m.b[1])), [m.b[1]]) self.assertEqual(list(identify_mutable_parameters(m.a + m.b[1])), [m.a, m.b[1]]) self.assertEqual(list(identify_mutable_parameters(m.a**m.b[1])), [m.a, m.b[1]]) self.assertEqual( list(identify_mutable_parameters(m.a**m.b[1] + m.b[2])), [m.a, m.b[1], m.b[2]]) self.assertEqual( list( identify_mutable_parameters(m.a**m.b[1] + m.b[2] * m.b[3] * m.b[2])), [m.a, m.b[1], m.b[2], m.b[3]]) self.assertEqual( list( identify_mutable_parameters(m.a**m.b[1] + m.b[2] / m.b[3] * m.b[2])), [m.a, m.b[1], m.b[2], m.b[3]]) # # Identify variables in the arguments to functions # self.assertEqual( list( identify_mutable_parameters( m.x(m.a, 'string_param', 1, []) * m.b[1])), [m.a, m.b[1]]) self.assertEqual( list( identify_mutable_parameters( m.x(m.p, 'string_param', 1, []) * m.b[1])), [m.b[1]]) self.assertEqual(list(identify_mutable_parameters(tanh(m.a) * m.b[1])), [m.a, m.b[1]]) self.assertEqual(list(identify_mutable_parameters(abs(m.a) * m.b[1])), [m.a, m.b[1]]) # # Check logic for allowing duplicates # self.assertEqual(list(identify_mutable_parameters(m.a**m.a + m.a)), [m.a])
def create_model(): m = ConcreteModel() m.name = 'Example 2: Yoshio' m.x1 = Var(initialize=0) m.x2 = Var(bounds=(-2.0, None), initialize=0) m.EF = ExternalFunction(ext_fcn, grad_ext_fcn) @m.Constraint() def con(m): return 2 * m.x1 + m.x2 + 10.0 == m.EF(m.x1, m.x2) m.obj = Objective(expr=(m.x1 - 1)**2 + (m.x2 - 3)**2 + m.EF(m.x1, m.x2)**2) return m
def test_1(self): ''' The simplest case that the black box has only two inputs and there is only one black block involved ''' def blackbox(a,b): return sin(a-b) m = self.m bb = ExternalFunction(blackbox) m.eflist = [bb] m.c1 = Constraint(expr=m.x[0] * m.z[0]**2 + bb(m.x[0],m.x[1]) == 2*sqrt(2.0)) pI = PyomoInterface(m, [bb], ConfigBlock()) self.assertEqual(pI.lx,2) self.assertEqual(pI.ly,1) self.assertEqual(pI.lz,3) self.assertEqual(len(list(identify_variables(m.c1.body))),3) self.assertEqual(len(list(identify_variables(m.c2.body))),2)
def delta_temperature_underwood_callback(b): """ This is a callback for a temperaure difference expression to calculate :math:`\Delta T` in the heat exchanger model using log-mean temperature difference (LMTD) approximation given by Underwood (1970). It can be supplied to "delta_temperature_callback" HeatExchanger configuration option. This uses a cube root function that works with negative numbers returning the real negative root. This should always evaluate successfully. """ # external function that ruturns the real root, for the cuberoot of negitive # numbers, so it will return without error for positive and negitive dT. b.cbrt = ExternalFunction(library=functions_lib(), function="cbrt") dT1 = b.delta_temperature_in dT2 = b.delta_temperature_out @b.Expression(b.flowsheet().config.time) def delta_temperature(b, t): return ((b.cbrt(dT1[t]) + b.cbrt(dT2[t]))/2.0)**3
def test_assert_units_consistent_all_components(self): # test all scalar components consistent u = units m = self._create_model_and_vars() m.obj = Objective(expr=m.dx / m.t - m.vx) m.con = Constraint(expr=m.dx / m.t == m.vx) # vars already added m.exp = Expression(expr=m.dx / m.t - m.vx) m.suff = Suffix(direction=Suffix.LOCAL) # params already added # sets already added m.rs = RangeSet(5) m.disj1 = Disjunct() m.disj1.constraint = Constraint(expr=m.dx / m.t <= m.vx) m.disj2 = Disjunct() m.disj2.constraint = Constraint(expr=m.dx / m.t <= m.vx) m.disjn = Disjunction(expr=[m.disj1, m.disj2]) # block tested as part of model m.extfn = ExternalFunction(python_callback_function, units=u.m / u.s, arg_units=[u.m, u.s]) m.conext = Constraint(expr=m.extfn(m.dx, m.t) - m.vx == 0) m.cset = ContinuousSet(bounds=(0, 1)) m.svar = Var(m.cset, units=u.m) m.dvar = DerivativeVar(sVar=m.svar, units=u.m / u.s) def prt1_rule(m): return {'avar': m.dx} def prt2_rule(m): return {'avar': m.dy} m.prt1 = Port(rule=prt1_rule) m.prt2 = Port(rule=prt2_rule) def arcrule(m): return dict(source=m.prt1, destination=m.prt2) m.arc = Arc(rule=arcrule) # complementarities do not work yet # The expression system removes the u.m since it is multiplied by zero. # We need to change the units_container to allow 0 when comparing units # m.compl = Complementarity(expr=complements(m.dx/m.t >= m.vx, m.dx == 0*u.m)) assert_units_consistent(m)
def create_model(): m = ConcreteModel() m.name = 'Example 1: Eason' m.z = Var(range(3), domain=Reals, initialize=2.) m.x = Var(range(2), initialize=2.) m.x[1] = 1.0 m.ext_fcn = ExternalFunction(ext_fcn, grad_ext_fcn) m.obj = Objective( expr=(m.z[0]-1.0)**2 + (m.z[0]-m.z[1])**2 + (m.z[2]-1.0)**2 \ + (m.x[0]-1.0)**4 + (m.x[1]-1.0)**6 ) m.c1 = Constraint(expr=m.x[0] * m.z[0]**2 + m.ext_fcn(m.x[0], m.x[1]) == 2 * sqrt(2.0)) m.c2 = Constraint(expr=m.z[2]**4 * m.z[1]**2 + m.z[1] == 8 + sqrt(2.0)) return m
def setUp(self): m = ConcreteModel() m.z = Var(range(3), domain=Reals, initialize=2.) m.x = Var(range(2), initialize=2.) m.x[1] = 1.0 def blackbox(a,b): return sin(a-b) self.bb = ExternalFunction(blackbox) m.obj = Objective( expr=(m.z[0]-1.0)**2 + (m.z[0]-m.z[1])**2 + (m.z[2]-1.0)**2 \ + (m.x[0]-1.0)**4 + (m.x[1]-1.0)**6 # + m.bb(m.x[0],m.x[1]) ) m.c1 = Constraint(expr=m.x[0] * m.z[0]**2 + self.bb(m.x[0],m.x[1]) == 2*sqrt(2.0)) m.c2 = Constraint(expr=m.z[2]**4 * m.z[1]**2 + m.z[1] == 8+sqrt(2.0)) self.m = m.clone()
def test_2(self): ''' The simplest case that the black box has only one inputs and there is only a formula ''' def blackbox(a): return sin(a) m = self.m bb = ExternalFunction(blackbox) m.eflist = [bb] m.c1 = Constraint(expr=m.x[0] * m.z[0]**2 + bb(m.x[0]-m.x[1]) == 2*sqrt(2.0)) pI = PyomoInterface(m, [bb], ConfigBlock()) self.assertEqual(pI.lx,1) self.assertEqual(pI.ly,1) self.assertEqual(pI.lz,5) self.assertEqual(len(list(identify_variables(m.c1.body))),3) self.assertEqual(len(list(identify_variables(m.c2.body))),2) self.assertEqual(len(m.tR.conset),1) self.assertEqual(len(list(identify_variables(m.tR.conset[1].body))),3)
def test_external_expression_constant(self): DLL = find_GSL() if not DLL: self.skipTest("Could not find the amplgsl.dll library") m = ConcreteModel() m.y = Var(initialize=4, bounds=(0, None)) m.hypot = ExternalFunction(library=DLL, function="gsl_hypot") m.o = Objective(expr=m.hypot(3, m.y)) self.assertAlmostEqual(value(m.o), 5.0, 7) baseline_fname, test_fname = self._get_fnames() self._cleanup(test_fname) m.write(test_fname, format='nl', io_options={'symbolic_solver_labels': True}) self.assertTrue(cmp(test_fname, baseline_fname), msg="Files %s and %s differ" % (test_fname, baseline_fname)) self._cleanup(test_fname)
def test_external_function(self): m = ConcreteModel() m.t = ContinuousSet(bounds=(0, 10)) def _fun(x): return x**2 m.x_func = ExternalFunction(_fun) m.y = Var(m.t, initialize=3) m.dy = DerivativeVar(m.y, initialize=3) def _con(m, t): return m.dy[t] == m.x_func(m.y[t]) m.con = Constraint(m.t, rule=_con) generate_finite_elements(m.t, 5) expand_components(m) self.assertEqual(len(m.y), 6) self.assertEqual(len(m.con), 6)
def delta_temperature_chen_callback(b): r""" This is a callback for a temperature difference expression to calculate :math:`\Delta T` in the heat exchanger model using log-mean temperature difference (LMTD) approximation given by Chen (1987). It can be supplied to "delta_temperature_callback" HeatExchanger configuration option. This uses a cube root function that works with negative numbers returning the real negative root. This should always evaluate successfully. """ dT1 = b.delta_temperature_in dT2 = b.delta_temperature_out temp_units = pyunits.get_units(dT1) # external function that ruturns the real root, for the cuberoot of negitive # numbers, so it will return without error for positive and negitive dT. b.cbrt = ExternalFunction(library=functions_lib(), function="cbrt", arg_units=[temp_units**3]) @b.Expression(b.flowsheet().time) def delta_temperature(b, t): return b.cbrt(dT1[t] * dT2[t] * 0.5 * (dT1[t] + dT2[t])) * temp_units
def test_execute_TRF(self): m = ConcreteModel() m.z = Var(range(3), domain=Reals, initialize=2.) m.x = Var(range(2), initialize=2.) m.x[1] = 1.0 def blackbox(a,b): return sin(a-b) bb = ExternalFunction(blackbox) m.obj = Objective( expr=(m.z[0]-1.0)**2 + (m.z[0]-m.z[1])**2 + (m.z[2]-1.0)**2 \ + (m.x[0]-1.0)**4 + (m.x[1]-1.0)**6 # + m.bb(m.x[0],m.x[1]) ) m.c1 = Constraint( expr=m.x[0] * m.z[0]**2 + bb(m.x[0],m.x[1]) == 2*sqrt(2.0)) m.c2 = Constraint(expr=m.z[2]**4 * m.z[1]**2 + m.z[1] == 8+sqrt(2.0)) SolverFactory('trustregion').solve(m, [bb]) self.assertAlmostEqual(value(m.obj), 0.277044789315, places=4) self.assertAlmostEqual(value(m.x[0]), 1.32193855369, places=4) self.assertAlmostEqual(value(m.x[1]), 0.628744699822, places=4)
def setUp(self): # Borrowed this test model from the trust region tests m = ConcreteModel(name="tm") m.z = Var(range(3), domain=Reals, initialize=2.) m.x = Var(range(2), initialize=2.) m.x[1] = 1.0 m.b1 = Block() m.b1.e1 = Expression(expr=m.x[0] + m.x[1]) def blackbox(a, b): return sin(a - b) self.bb = ExternalFunction(blackbox) m.obj = Objective( expr=(m.z[0]-1.0)**2 + (m.z[0]-m.z[1])**2 + (m.z[2]-1.0)**2 \ + (m.x[0]-1.0)**4 + (m.x[1]-1.0)**6 # + m.bb(m.x[0],m.x[1]) ) m.c1 = Constraint(expr=m.x[0] * m.z[0]**2 + self.bb(m.x[0], m.x[1]) == 2 * sqrt(2.0)) m.c2 = Constraint(expr=m.z[2]**4 * m.z[1]**2 + m.z[1] == 8 + sqrt(2.0)) self.m = m.clone()
# ___________________________________________________________________________ from pyomo.environ import ConcreteModel, Var, Reals, ExternalFunction, sin, sqrt, Constraint, Objective from pyomo.opt import SolverFactory m = ConcreteModel() m.z = Var(range(3), domain=Reals, initialize=2.) m.x = Var(range(2), initialize=2.) m.x[1] = 1.0 def blackbox(a, b): return sin(a - b) bb = ExternalFunction(blackbox) m.obj = Objective( expr=(m.z[0]-1.0)**2 + (m.z[0]-m.z[1])**2 + (m.z[2]-1.0)**2 \ + (m.x[0]-1.0)**4 + (m.x[1]-1.0)**6 # + m.bb(m.x[0],m.x[1]) ) m.c1 = Constraint(expr=m.x[0] * m.z[0]**2 + bb(m.x[0], m.x[1]) == 2 * sqrt(2.0)) m.c2 = Constraint(expr=m.z[2]**4 * m.z[1]**2 + m.z[1] == 8 + sqrt(2.0)) m.pprint() optTRF = SolverFactory('trustregion') optTRF.solve(m, [bb]) m.display()
def build(self): """ Building model Args: None Returns: None """ # Call UnitModel.build to setup dynamics super().build() config = self.config # Add variables self.overall_heat_transfer_coefficient = Var( self.flowsheet().config.time, domain=PositiveReals, initialize=100, doc="Overall heat transfer coefficient") self.overall_heat_transfer_coefficient.latex_symbol = "U" self.area = Var(domain=PositiveReals, initialize=1000, doc="Heat exchange area") self.area.latex_symbol = "A" if config.flow_pattern == HeatExchangerFlowPattern.crossflow: self.crossflow_factor = Var( self.flowsheet().config.time, initialize=1, doc="Factor to adjust coutercurrent flow heat transfer " "calculation for cross flow.") if config.delta_temperature_rule == delta_temperature_underwood2_rule: # Define a cube root function that return the real negative root # for the cube root of a negative number. self.cbrt = ExternalFunction(library=functions_lib(), function="cbrt") # Add Control Volumes _make_heater_control_volume(self, "side_1", config.side_1, dynamic=config.dynamic, has_holdup=config.has_holdup) _make_heater_control_volume(self, "side_2", config.side_2, dynamic=config.dynamic, has_holdup=config.has_holdup) # Add Ports self.add_inlet_port(name="inlet_1", block=self.side_1) self.add_inlet_port(name="inlet_2", block=self.side_2) self.add_outlet_port(name="outlet_1", block=self.side_1) self.add_outlet_port(name="outlet_2", block=self.side_2) # Add convienient references to heat duty. add_object_reference(self, "heat_duty", self.side_2.heat) self.side_1.heat.latex_symbol = "Q_1" self.side_2.heat.latex_symbol = "Q_2" @self.Expression(self.flowsheet().config.time, doc="Temperature difference at the side 1 inlet end") def delta_temperature_in(b, t): if b.config.flow_pattern == \ HeatExchangerFlowPattern.countercurrent: return b.side_1.properties_in[t].temperature -\ b.side_2.properties_out[t].temperature elif b.config.flow_pattern == HeatExchangerFlowPattern.cocurrent: return b.side_1.properties_in[t].temperature -\ b.side_2.properties_in[t].temperature elif b.config.flow_pattern == HeatExchangerFlowPattern.crossflow: return b.side_1.properties_in[t].temperature -\ b.side_2.properties_out[t].temperature else: raise ConfigurationError( "Flow pattern {} not supported".format( b.config.flow_pattern)) @self.Expression(self.flowsheet().config.time, doc="Temperature difference at the side 1 outlet end") def delta_temperature_out(b, t): if b.config.flow_pattern == \ HeatExchangerFlowPattern.countercurrent: return b.side_1.properties_out[t].temperature -\ b.side_2.properties_in[t].temperature elif b.config.flow_pattern == HeatExchangerFlowPattern.cocurrent: return b.side_1.properties_out[t].temperature -\ b.side_2.properties_out[t].temperature elif b.config.flow_pattern == HeatExchangerFlowPattern.crossflow: return b.side_1.properties_out[t].temperature -\ b.side_2.properties_in[t].temperature # Add a unit level energy balance def unit_heat_balance_rule(b, t): return 0 == self.side_1.heat[t] + self.side_2.heat[t] self.unit_heat_balance = Constraint(self.flowsheet().config.time, rule=unit_heat_balance_rule) # Add heat transfer equation self.delta_temperature = Expression( self.flowsheet().config.time, rule=config.delta_temperature_rule, doc="Temperature difference driving force for heat transfer") self.delta_temperature.latex_symbol = "\\Delta T" if config.flow_pattern == HeatExchangerFlowPattern.crossflow: self.heat_transfer_equation = Constraint( self.flowsheet().config.time, rule=_cross_flow_heat_transfer_rule) else: self.heat_transfer_equation = Constraint( self.flowsheet().config.time, rule=_heat_transfer_rule)
def test_as_quantity_expression(self): _pint = units._pint_registry Quantity = _pint.Quantity m = ConcreteModel() m.x = Var(initialize=1) m.y = Var(initialize=2, units=units.g) m.p = Param(initialize=3) m.q = Param(initialize=4, units=1 / units.s) q = as_quantity(m.x * m.p) self.assertIs(q.__class__, Quantity) self.assertEqual(q, 3 * _pint.dimensionless) q = as_quantity(m.x * m.q) self.assertIs(q.__class__, Quantity) self.assertEqual(q, 4 / _pint.s) q = as_quantity(m.y * m.p) self.assertIs(q.__class__, Quantity) self.assertEqual(q, 6 * _pint.g) q = as_quantity(m.y * m.q) self.assertIs(q.__class__, Quantity) self.assertEqual(q, 8 * _pint.g / _pint.s) q = as_quantity(m.y <= 2 * m.y) self.assertIs(q.__class__, bool) self.assertEqual(q, True) q = as_quantity(m.y >= 2 * m.y) self.assertIs(q.__class__, bool) self.assertEqual(q, False) q = as_quantity(EXPR.Expr_if(IF=m.y <= 2 * m.y, THEN=m.x, ELSE=m.p)) self.assertIs(q.__class__, Quantity) self.assertEqual(q, 1 * _pint.dimensionless) q = as_quantity(EXPR.Expr_if(IF=m.y >= 2 * m.y, THEN=m.x, ELSE=m.p)) self.assertIs(q.__class__, Quantity) self.assertEqual(q, 3 * _pint.dimensionless) # NOTE: The following two tests are not unit consistent (but can # be evaluated) q = as_quantity(EXPR.Expr_if(IF=m.x <= 2 * m.x, THEN=m.y, ELSE=m.q)) self.assertIs(q.__class__, Quantity) self.assertEqual(q, 2 * _pint.g) q = as_quantity(EXPR.Expr_if(IF=m.x >= 2 * m.x, THEN=m.y, ELSE=m.q)) self.assertIs(q.__class__, Quantity) self.assertEqual(q, 4 / _pint.s) # Note: check the units explicitly, as # Quantity(x, radian) == Quantity(x, dimensionless) q = as_quantity(acos(m.x)) self.assertIs(q.__class__, Quantity) self.assertEqual(q.units, _pint.radian) self.assertEqual(q, 0 * _pint.radian) q = as_quantity(cos(m.x * math.pi)) self.assertIs(q.__class__, Quantity) self.assertEqual(q.units, _pint.dimensionless) self.assertAlmostEqual(q, -1 * _pint.dimensionless) def MyAdder(x, y): return x + y m.EF = ExternalFunction(MyAdder, units=units.kg) ef = m.EF(m.x, m.y) q = as_quantity(ef) self.assertIs(q.__class__, Quantity) self.assertAlmostEqual(q, 3 * _pint.kg)
def test_get_check_units_on_all_expressions(self): # this method is going to test all the expression types that should work # to be defensive, we will also test that we actually have the expected expression type # therefore, if the expression system changes and we get a different expression type, # we will know we need to change these tests uc = units kg = uc.kg m = uc.m model = ConcreteModel() model.x = Var() model.y = Var() model.z = Var() model.p = Param(initialize=42.0, mutable=True) model.xkg = Var(units=kg) model.ym = Var(units=m) # test equality self._get_check_units_ok(3.0*kg == 1.0*kg, uc, 'kg', EXPR.EqualityExpression) self._get_check_units_fail(3.0*kg == 2.0*m, uc, EXPR.EqualityExpression) # test inequality self._get_check_units_ok(3.0*kg <= 1.0*kg, uc, 'kg', EXPR.InequalityExpression) self._get_check_units_fail(3.0*kg <= 2.0*m, uc, EXPR.InequalityExpression) self._get_check_units_ok(3.0*kg >= 1.0*kg, uc, 'kg', EXPR.InequalityExpression) self._get_check_units_fail(3.0*kg >= 2.0*m, uc, EXPR.InequalityExpression) # test RangedExpression self._get_check_units_ok(inequality(3.0*kg, 4.0*kg, 5.0*kg), uc, 'kg', EXPR.RangedExpression) self._get_check_units_fail(inequality(3.0*m, 4.0*kg, 5.0*kg), uc, EXPR.RangedExpression) self._get_check_units_fail(inequality(3.0*kg, 4.0*m, 5.0*kg), uc, EXPR.RangedExpression) self._get_check_units_fail(inequality(3.0*kg, 4.0*kg, 5.0*m), uc, EXPR.RangedExpression) # test SumExpression, NPV_SumExpression self._get_check_units_ok(3.0*model.x*kg + 1.0*model.y*kg + 3.65*model.z*kg, uc, 'kg', EXPR.SumExpression) self._get_check_units_fail(3.0*model.x*kg + 1.0*model.y*m + 3.65*model.z*kg, uc, EXPR.SumExpression) self._get_check_units_ok(3.0*kg + 1.0*kg + 2.0*kg, uc, 'kg', EXPR.NPV_SumExpression) self._get_check_units_fail(3.0*kg + 1.0*kg + 2.0*m, uc, EXPR.NPV_SumExpression) # test ProductExpression, NPV_ProductExpression self._get_check_units_ok(model.x*kg * model.y*m, uc, 'kg*m', EXPR.ProductExpression) self._get_check_units_ok(3.0*kg * 1.0*m, uc, 'kg*m', EXPR.NPV_ProductExpression) self._get_check_units_ok(3.0*kg*m, uc, 'kg*m', EXPR.NPV_ProductExpression) # I don't think that there are combinations that can "fail" for products # test MonomialTermExpression self._get_check_units_ok(model.x*kg, uc, 'kg', EXPR.MonomialTermExpression) # test DivisionExpression, NPV_DivisionExpression self._get_check_units_ok(1.0/(model.x*kg), uc, '1/kg', EXPR.DivisionExpression) self._get_check_units_ok(2.0/kg, uc, '1/kg', EXPR.NPV_DivisionExpression) self._get_check_units_ok((model.x*kg)/1.0, uc, 'kg', EXPR.MonomialTermExpression) self._get_check_units_ok(kg/2.0, uc, 'kg', EXPR.NPV_DivisionExpression) self._get_check_units_ok(model.y*m/(model.x*kg), uc, 'm/kg', EXPR.DivisionExpression) self._get_check_units_ok(m/kg, uc, 'm/kg', EXPR.NPV_DivisionExpression) # I don't think that there are combinations that can "fail" for products # test PowExpression, NPV_PowExpression # ToDo: fix the str representation to combine the powers or the expression system self._get_check_units_ok((model.x*kg**2)**3, uc, 'kg**6', EXPR.PowExpression) # would want this to be kg**6 self._get_check_units_fail(kg**model.x, uc, EXPR.PowExpression, UnitsError) self._get_check_units_fail(model.x**kg, uc, EXPR.PowExpression, UnitsError) self._get_check_units_ok(kg**2, uc, 'kg**2', EXPR.NPV_PowExpression) self._get_check_units_fail(3.0**kg, uc, EXPR.NPV_PowExpression, UnitsError) # test NegationExpression, NPV_NegationExpression self._get_check_units_ok(-(kg*model.x*model.y), uc, 'kg', EXPR.NegationExpression) self._get_check_units_ok(-kg, uc, 'kg', EXPR.NPV_NegationExpression) # don't think there are combinations that fan "fail" for negation # test AbsExpression, NPV_AbsExpression self._get_check_units_ok(abs(kg*model.x), uc, 'kg', EXPR.AbsExpression) self._get_check_units_ok(abs(kg), uc, 'kg', EXPR.NPV_AbsExpression) # don't think there are combinations that fan "fail" for abs # test the different UnaryFunctionExpression / NPV_UnaryFunctionExpression types # log self._get_check_units_ok(log(3.0*model.x), uc, None, EXPR.UnaryFunctionExpression) self._get_check_units_fail(log(3.0*kg*model.x), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_ok(log(3.0*model.p), uc, None, EXPR.NPV_UnaryFunctionExpression) self._get_check_units_fail(log(3.0*kg), uc, EXPR.NPV_UnaryFunctionExpression, UnitsError) # log10 self._get_check_units_ok(log10(3.0*model.x), uc, None, EXPR.UnaryFunctionExpression) self._get_check_units_fail(log10(3.0*kg*model.x), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_ok(log10(3.0*model.p), uc, None, EXPR.NPV_UnaryFunctionExpression) self._get_check_units_fail(log10(3.0*kg), uc, EXPR.NPV_UnaryFunctionExpression, UnitsError) # sin self._get_check_units_ok(sin(3.0*model.x*uc.radians), uc, None, EXPR.UnaryFunctionExpression) self._get_check_units_fail(sin(3.0*kg*model.x), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_fail(sin(3.0*kg*model.x*uc.kg), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_ok(sin(3.0*model.p*uc.radians), uc, None, EXPR.NPV_UnaryFunctionExpression) self._get_check_units_fail(sin(3.0*kg), uc, EXPR.NPV_UnaryFunctionExpression, UnitsError) # cos self._get_check_units_ok(cos(3.0*model.x*uc.radians), uc, None, EXPR.UnaryFunctionExpression) self._get_check_units_fail(cos(3.0*kg*model.x), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_fail(cos(3.0*kg*model.x*uc.kg), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_ok(cos(3.0*model.p*uc.radians), uc, None, EXPR.NPV_UnaryFunctionExpression) self._get_check_units_fail(cos(3.0*kg), uc, EXPR.NPV_UnaryFunctionExpression, UnitsError) # tan self._get_check_units_ok(tan(3.0*model.x*uc.radians), uc, None, EXPR.UnaryFunctionExpression) self._get_check_units_fail(tan(3.0*kg*model.x), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_fail(tan(3.0*kg*model.x*uc.kg), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_ok(tan(3.0*model.p*uc.radians), uc, None, EXPR.NPV_UnaryFunctionExpression) self._get_check_units_fail(tan(3.0*kg), uc, EXPR.NPV_UnaryFunctionExpression, UnitsError) # sin self._get_check_units_ok(sinh(3.0*model.x*uc.radians), uc, None, EXPR.UnaryFunctionExpression) self._get_check_units_fail(sinh(3.0*kg*model.x), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_fail(sinh(3.0*kg*model.x*uc.kg), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_ok(sinh(3.0*model.p*uc.radians), uc, None, EXPR.NPV_UnaryFunctionExpression) self._get_check_units_fail(sinh(3.0*kg), uc, EXPR.NPV_UnaryFunctionExpression, UnitsError) # cos self._get_check_units_ok(cosh(3.0*model.x*uc.radians), uc, None, EXPR.UnaryFunctionExpression) self._get_check_units_fail(cosh(3.0*kg*model.x), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_fail(cosh(3.0*kg*model.x*uc.kg), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_ok(cosh(3.0*model.p*uc.radians), uc, None, EXPR.NPV_UnaryFunctionExpression) self._get_check_units_fail(cosh(3.0*kg), uc, EXPR.NPV_UnaryFunctionExpression, UnitsError) # tan self._get_check_units_ok(tanh(3.0*model.x*uc.radians), uc, None, EXPR.UnaryFunctionExpression) self._get_check_units_fail(tanh(3.0*kg*model.x), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_fail(tanh(3.0*kg*model.x*uc.kg), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_ok(tanh(3.0*model.p*uc.radians), uc, None, EXPR.NPV_UnaryFunctionExpression) self._get_check_units_fail(tanh(3.0*kg), uc, EXPR.NPV_UnaryFunctionExpression, UnitsError) # asin self._get_check_units_ok(asin(3.0*model.x), uc, 'rad', EXPR.UnaryFunctionExpression) self._get_check_units_fail(asin(3.0*kg*model.x), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_ok(asin(3.0*model.p), uc, 'rad', EXPR.NPV_UnaryFunctionExpression) self._get_check_units_fail(asin(3.0*model.p*kg), uc, EXPR.NPV_UnaryFunctionExpression, UnitsError) # acos self._get_check_units_ok(acos(3.0*model.x), uc, 'rad', EXPR.UnaryFunctionExpression) self._get_check_units_fail(acos(3.0*kg*model.x), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_ok(acos(3.0*model.p), uc, 'rad', EXPR.NPV_UnaryFunctionExpression) self._get_check_units_fail(acos(3.0*model.p*kg), uc, EXPR.NPV_UnaryFunctionExpression, UnitsError) # atan self._get_check_units_ok(atan(3.0*model.x), uc, 'rad', EXPR.UnaryFunctionExpression) self._get_check_units_fail(atan(3.0*kg*model.x), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_ok(atan(3.0*model.p), uc, 'rad', EXPR.NPV_UnaryFunctionExpression) self._get_check_units_fail(atan(3.0*model.p*kg), uc, EXPR.NPV_UnaryFunctionExpression, UnitsError) # exp self._get_check_units_ok(exp(3.0*model.x), uc, None, EXPR.UnaryFunctionExpression) self._get_check_units_fail(exp(3.0*kg*model.x), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_ok(exp(3.0*model.p), uc, None, EXPR.NPV_UnaryFunctionExpression) self._get_check_units_fail(exp(3.0*kg), uc, EXPR.NPV_UnaryFunctionExpression, UnitsError) # sqrt self._get_check_units_ok(sqrt(3.0*model.x), uc, None, EXPR.UnaryFunctionExpression) self._get_check_units_ok(sqrt(3.0*model.x*kg**2), uc, 'kg', EXPR.UnaryFunctionExpression) self._get_check_units_ok(sqrt(3.0*model.x*kg), uc, 'kg**0.5', EXPR.UnaryFunctionExpression) self._get_check_units_ok(sqrt(3.0*model.p), uc, None, EXPR.NPV_UnaryFunctionExpression) self._get_check_units_ok(sqrt(3.0*model.p*kg**2), uc, 'kg', EXPR.NPV_UnaryFunctionExpression) self._get_check_units_ok(sqrt(3.0*model.p*kg), uc, 'kg**0.5', EXPR.NPV_UnaryFunctionExpression) # asinh self._get_check_units_ok(asinh(3.0*model.x), uc, 'rad', EXPR.UnaryFunctionExpression) self._get_check_units_fail(asinh(3.0*kg*model.x), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_ok(asinh(3.0*model.p), uc, 'rad', EXPR.NPV_UnaryFunctionExpression) self._get_check_units_fail(asinh(3.0*model.p*kg), uc, EXPR.NPV_UnaryFunctionExpression, UnitsError) # acosh self._get_check_units_ok(acosh(3.0*model.x), uc, 'rad', EXPR.UnaryFunctionExpression) self._get_check_units_fail(acosh(3.0*kg*model.x), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_ok(acosh(3.0*model.p), uc, 'rad', EXPR.NPV_UnaryFunctionExpression) self._get_check_units_fail(acosh(3.0*model.p*kg), uc, EXPR.NPV_UnaryFunctionExpression, UnitsError) # atanh self._get_check_units_ok(atanh(3.0*model.x), uc, 'rad', EXPR.UnaryFunctionExpression) self._get_check_units_fail(atanh(3.0*kg*model.x), uc, EXPR.UnaryFunctionExpression, UnitsError) self._get_check_units_ok(atanh(3.0*model.p), uc, 'rad', EXPR.NPV_UnaryFunctionExpression) self._get_check_units_fail(atanh(3.0*model.p*kg), uc, EXPR.NPV_UnaryFunctionExpression, UnitsError) # ceil self._get_check_units_ok(ceil(kg*model.x), uc, 'kg', EXPR.UnaryFunctionExpression) self._get_check_units_ok(ceil(kg), uc, 'kg', EXPR.NPV_UnaryFunctionExpression) # don't think there are combinations that fan "fail" for ceil # floor self._get_check_units_ok(floor(kg*model.x), uc, 'kg', EXPR.UnaryFunctionExpression) self._get_check_units_ok(floor(kg), uc, 'kg', EXPR.NPV_UnaryFunctionExpression) # don't think there are combinations that fan "fail" for floor # test Expr_ifExpression # consistent if, consistent then/else self._get_check_units_ok(EXPR.Expr_if(IF=model.x*kg + kg >= 2.0*kg, THEN=model.x*kg, ELSE=model.y*kg), uc, 'kg', EXPR.Expr_ifExpression) # unitless if, consistent then/else self._get_check_units_ok(EXPR.Expr_if(IF=model.x >= 2.0, THEN=model.x*kg, ELSE=model.y*kg), uc, 'kg', EXPR.Expr_ifExpression) # consistent if, unitless then/else self._get_check_units_ok(EXPR.Expr_if(IF=model.x*kg + kg >= 2.0*kg, THEN=model.x, ELSE=model.x), uc, None, EXPR.Expr_ifExpression) # inconsistent then/else self._get_check_units_fail(EXPR.Expr_if(IF=model.x >= 2.0, THEN=model.x*m, ELSE=model.y*kg), uc, EXPR.Expr_ifExpression) # inconsistent then/else NPV self._get_check_units_fail(EXPR.Expr_if(IF=model.x >= 2.0, THEN=model.p*m, ELSE=model.p*kg), uc, EXPR.Expr_ifExpression) # inconsistent then/else NPV units only self._get_check_units_fail(EXPR.Expr_if(IF=model.x >= 2.0, THEN=m, ELSE=kg), uc, EXPR.Expr_ifExpression) # test EXPR.IndexTemplate and GetItemExpression model.S = Set() i = EXPR.IndexTemplate(model.S) j = EXPR.IndexTemplate(model.S) self._get_check_units_ok(i, uc, None, EXPR.IndexTemplate) model.mat = Var(model.S, model.S) self._get_check_units_ok(model.mat[i,j+1], uc, None, EXPR.GetItemExpression) # test ExternalFunctionExpression, NPV_ExternalFunctionExpression model.ef = ExternalFunction(python_callback_function) self._get_check_units_ok(model.ef(model.x, model.y), uc, None, EXPR.ExternalFunctionExpression) self._get_check_units_ok(model.ef(1.0, 2.0), uc, None, EXPR.NPV_ExternalFunctionExpression) self._get_check_units_fail(model.ef(model.x*kg, model.y), uc, EXPR.ExternalFunctionExpression, UnitsError) self._get_check_units_fail(model.ef(2.0*kg, 1.0), uc, EXPR.NPV_ExternalFunctionExpression, UnitsError) # test ExternalFunctionExpression, NPV_ExternalFunctionExpression model.ef2 = ExternalFunction(python_callback_function, units=uc.kg) self._get_check_units_ok(model.ef2(model.x, model.y), uc, 'kg', EXPR.ExternalFunctionExpression) self._get_check_units_ok(model.ef2(1.0, 2.0), uc, 'kg', EXPR.NPV_ExternalFunctionExpression) self._get_check_units_fail(model.ef2(model.x*kg, model.y), uc, EXPR.ExternalFunctionExpression, UnitsError) self._get_check_units_fail(model.ef2(2.0*kg, 1.0), uc, EXPR.NPV_ExternalFunctionExpression, UnitsError) # test ExternalFunctionExpression, NPV_ExternalFunctionExpression model.ef3 = ExternalFunction(python_callback_function, units=uc.kg, arg_units=[uc.kg, uc.m]) self._get_check_units_fail(model.ef3(model.x, model.y), uc, EXPR.ExternalFunctionExpression) self._get_check_units_fail(model.ef3(1.0, 2.0), uc, EXPR.NPV_ExternalFunctionExpression) self._get_check_units_fail(model.ef3(model.x*kg, model.y), uc, EXPR.ExternalFunctionExpression, UnitsError) self._get_check_units_fail(model.ef3(2.0*kg, 1.0), uc, EXPR.NPV_ExternalFunctionExpression, UnitsError) self._get_check_units_ok(model.ef3(2.0*kg, 1.0*uc.m), uc, 'kg', EXPR.NPV_ExternalFunctionExpression) self._get_check_units_ok(model.ef3(model.x*kg, model.y*m), uc, 'kg', EXPR.ExternalFunctionExpression) self._get_check_units_ok(model.ef3(model.xkg, model.ym), uc, 'kg', EXPR.ExternalFunctionExpression) self._get_check_units_fail(model.ef3(model.ym, model.xkg), uc, EXPR.ExternalFunctionExpression, InconsistentUnitsError)
def common(b, pobj): # TODO: determine if Henry's Law applies to Cubic EoS systems # For now, raise an exception if found # Follow on questions: # If Henry's law is used for a component, how does that effect # calculating A, B and phi? for j in b.component_list: cobj = b.params.get_component(j) if (cobj.config.henry_component is not None and pobj.local_name in cobj.config.henry_component): raise PropertyNotSupportedError( "{} Cubic equations of state do not support Henry's " "components [{}, {}].".format(b.name, pobj.local_name, j)) ctype = pobj._cubic_type cname = pobj.config.equation_of_state_options["type"].name mixing_rule_a = pobj._mixing_rule_a mixing_rule_b = pobj._mixing_rule_b if hasattr(b, cname + "_fw"): # Common components already constructed by previous phase return # Create expressions for coefficients def rule_fw(m, j): func_fw = getattr(m.params, cname + "_func_fw") cobj = m.params.get_component(j) return func_fw(cobj) b.add_component( cname + '_fw', Expression(b.component_list, rule=rule_fw, doc='EoS S factor')) def rule_a_crit(m, j): cobj = m.params.get_component(j) return (EoS_param[ctype]['omegaA'] * ((Cubic.gas_constant(b) * cobj.temperature_crit)**2 / cobj.pressure_crit)) b.add_component( cname + '_a_crit', Expression(b.component_list, rule=rule_a_crit, doc='Component a coefficient at T_crit')) def rule_a(m, j): cobj = m.params.get_component(j) fw = getattr(m, cname + "_fw")[j] ac = getattr(m, cname + '_a_crit')[j] func_alpha = getattr(m.params, cname + "_func_alpha") return ac * func_alpha(m.temperature, fw, cobj) b.add_component( cname + '_a', Expression(b.component_list, rule=rule_a, doc='Component a coefficient')) def rule_da_dT(m, j): cobj = m.params.get_component(j) fw = getattr(m, cname + "_fw")[j] ac = getattr(m, cname + '_a_crit')[j] func_dalpha_dT = getattr(m.params, cname + "_func_dalpha_dT") return ac * func_dalpha_dT(m.temperature, fw, cobj) b.add_component( cname + '_da_dT', Expression(b.component_list, rule=rule_da_dT, doc='Temperature derivative of component a')) def rule_d2a_dT2(m, j): cobj = m.params.get_component(j) fw = getattr(m, cname + "_fw")[j] ac = getattr(m, cname + '_a_crit')[j] func_d2alpha_dT2 = getattr(m.params, cname + "_func_d2alpha_dT2") return ac * func_d2alpha_dT2(m.temperature, fw, cobj) b.add_component( cname + '_d2a_dT2', Expression(b.component_list, rule=rule_d2a_dT2, doc='Second temperature derivative' 'of component a')) def func_b(m, j): cobj = m.params.get_component(j) return (EoS_param[ctype]['coeff_b'] * Cubic.gas_constant(b) * cobj.temperature_crit / cobj.pressure_crit) b.add_component( cname + '_b', Expression(b.component_list, rule=func_b, doc='Component b coefficient')) if mixing_rule_a == MixingRuleA.default: def rule_am(m, p): a = getattr(m, cname + "_a") return rule_am_default(m, cname, a, p) b.add_component(cname + '_am', Expression(b.phase_list, rule=rule_am)) def rule_daij_dT(m, i, j): a = getattr(m, cname + "_a") da_dT = getattr(m, cname + "_da_dT") k = getattr(m.params, cname + "_kappa") # Include temperature derivative of k for future extension dk_ij_dT = 0 return sqrt( a[i] * a[j]) * (-dk_ij_dT + (1 - k[i, j]) / 2 * (da_dT[i] / a[i] + da_dT[j] / a[j])) b.add_component( cname + '_daij_dT', Expression(b.component_list, b.component_list, rule=rule_daij_dT)) def rule_dam_dT(m, p): daij_dT = getattr(m, cname + "_daij_dT") return sum( sum(m.mole_frac_phase_comp[p, i] * m.mole_frac_phase_comp[p, j] * daij_dT[i, j] for j in m.components_in_phase(p)) for i in m.components_in_phase(p)) b.add_component(cname + "_dam_dT", Expression(b.phase_list, rule=rule_dam_dT)) def rule_d2am_dT2(m, p): k = getattr(m.params, cname + "_kappa") a = getattr(m, cname + "_a") da_dT = getattr(m, cname + "_da_dT") d2a_dT2 = getattr(m, cname + "_d2a_dT2") # Placeholders for if temperature dependent k is needed dk_dT = 0 d2k_dT2 = 0 # Initialize loop variable d2am_dT2 = 0 for i in m.components_in_phase(p): for j in m.components_in_phase(p): d2aij_dT2 = ( sqrt(a[i] * a[j]) * (-d2k_dT2 - dk_dT * (da_dT[i] / a[i] + da_dT[j] / a[j]) + (1 - k[i, j]) / 2 * (d2a_dT2[i] / a[i] + d2a_dT2[j] / a[j] - 1 / 2 * (da_dT[i] / a[i] - da_dT[j] / a[j])**2))) d2am_dT2 += (m.mole_frac_phase_comp[p, i] * m.mole_frac_phase_comp[p, j] * d2aij_dT2) return d2am_dT2 b.add_component(cname + "_d2am_dT2", Expression(b.phase_list, rule=rule_d2am_dT2)) def rule_delta(m, p, i): # See pg. 145 in Properties of Gases and Liquids a = getattr(m, cname + "_a") am = getattr(m, cname + "_am") kappa = getattr(m.params, cname + "_kappa") return (2 * sqrt(a[i]) / am[p] * sum(m.mole_frac_phase_comp[p, j] * sqrt(a[j]) * (1 - kappa[i, j]) for j in b.components_in_phase(p))) b.add_component(cname + "_delta", Expression(b.phase_component_set, rule=rule_delta)) else: raise ConfigurationError( "{} Unrecognized option for Equation of State " "mixing_rule_a: {}. Must be an instance of MixingRuleA " "Enum.".format(b.name, mixing_rule_a)) if mixing_rule_b == MixingRuleB.default: def rule_bm(m, p): b = getattr(m, cname + "_b") return rule_bm_default(m, b, p) b.add_component(cname + '_bm', Expression(b.phase_list, rule=rule_bm)) else: raise ConfigurationError( "{} Unrecognized option for Equation of State " "mixing_rule_a: {}. Must be an instance of MixingRuleB " "Enum.".format(b.name, mixing_rule_b)) def rule_A(m, p): am = getattr(m, cname + "_am") return (am[p] * m.pressure / (Cubic.gas_constant(b) * m.temperature)**2) b.add_component(cname + '_A', Expression(b.phase_list, rule=rule_A)) def rule_B(m, p): bm = getattr(m, cname + "_bm") return (bm[p] * m.pressure / (Cubic.gas_constant(b) * m.temperature)) b.add_component(cname + '_B', Expression(b.phase_list, rule=rule_B)) # Add components at equilibrium state if required if (b.params.config.phases_in_equilibrium is not None and (not b.config.defined_state or b.always_flash)): def func_a_eq(m, p1, p2, j): cobj = m.params.get_component(j) fw = getattr(m, cname + "_fw")[j] ac = getattr(m, cname + '_a_crit')[j] func_alpha = getattr(m.params, cname + "_func_alpha") return ac * func_alpha(m._teq[p1, p2], fw, cobj) b.add_component( '_' + cname + '_a_eq', Expression(b.params._pe_pairs, b.component_list, rule=func_a_eq, doc='Component a coefficient at Teq')) def rule_am_eq(m, p1, p2, p3): try: rule = m.params.get_phase( p3).config.equation_of_state_options["mixing_rule_a"] except (KeyError, TypeError): rule = MixingRuleA.default a = getattr(m, "_" + cname + "_a_eq") if rule == MixingRuleA.default: return rule_am_default(m, cname, a, p3, (p1, p2)) else: raise ConfigurationError( "{} Unrecognized option for Equation of State " "mixing_rule_a: {}. Must be an instance of MixingRuleA " "Enum.".format(m.name, rule)) b.add_component( '_' + cname + '_am_eq', Expression(b.params._pe_pairs, b.phase_list, rule=rule_am_eq)) def rule_A_eq(m, p1, p2, p3): am_eq = getattr(m, "_" + cname + "_am_eq") return (am_eq[p1, p2, p3] * m.pressure / (Cubic.gas_constant(b) * m._teq[p1, p2])**2) b.add_component( '_' + cname + '_A_eq', Expression(b.params._pe_pairs, b.phase_list, rule=rule_A_eq)) def rule_B_eq(m, p1, p2, p3): bm = getattr(m, cname + "_bm") return (bm[p3] * m.pressure / (Cubic.gas_constant(b) * m._teq[p1, p2])) b.add_component( '_' + cname + '_B_eq', Expression(b.params._pe_pairs, b.phase_list, rule=rule_B_eq)) def rule_delta_eq(m, p1, p2, p3, i): # See pg. 145 in Properties of Gases and Liquids a = getattr(m, "_" + cname + "_a_eq") am = getattr(m, "_" + cname + "_am_eq") kappa = getattr(m.params, cname + "_kappa") return ( 2 * sqrt(a[p1, p2, i]) / am[p1, p2, p3] * sum(m.mole_frac_phase_comp[p3, j] * sqrt(a[p1, p2, j]) * (1 - kappa[i, j]) for j in m.components_in_phase(p3))) b.add_component( "_" + cname + "_delta_eq", Expression(b.params._pe_pairs, b.phase_component_set, rule=rule_delta_eq)) # Set up external function calls b.add_component("_" + cname + "_ext_func_param", Param(default=ctype.value)) b.add_component("_" + cname + "_proc_Z_liq", ExternalFunction(library=_so, function="ceos_z_liq")) b.add_component("_" + cname + "_proc_Z_vap", ExternalFunction(library=_so, function="ceos_z_vap"))
Al = 0.0419062 Bl = 0.0262770 Av = 0.1180005 Bv = 0.0262769 Zl = 0.9870125 Zv = 0.9067390 Al_eq = 0.8325554 Bl_eq = 0.0788309 Av_eq = 1.9805792 Bv_eq = 0.0788309 # Set path to root finder .so file _so = os.path.join(bin_directory, "cubic_roots.so") f_Zl = ExternalFunction(library=_so, function="ceos_z_liq") f_Zv = ExternalFunction(library=_so, function="ceos_z_vap") @pytest.mark.unit def test_common(m): # Test cubic components assert isinstance(m.props[1].PR_fw, Expression) assert len(m.props[1].PR_fw) == len(m.params.component_list) for i in m.params.component_list: omega = m.params.get_component(i).omega assert value(m.props[1].PR_fw[i]) == value( 0.37464 + 1.54226*omega - 0.26992*omega**2) assert isinstance(m.props[1].PR_a, Expression) assert len(m.props[1].PR_a) == len(m.params.component_list)
def common(b, pobj): ctype = pobj._cubic_type cname = pobj.config.equation_of_state_options["type"].name if hasattr(b, cname + "_fw"): # Common components already constructed by previous phase return # Create expressions for coefficients def func_fw(m, j): cobj = m.params.get_component(j) if ctype == CubicType.PR: return 0.37464 + 1.54226*cobj.omega - \ 0.26992*cobj.omega**2 elif ctype == CubicType.SRK: return 0.48 + 1.574*cobj.omega - \ 0.176*cobj.omega**2 else: raise BurntToast( "{} received unrecognized cubic type. This should " "never happen, so please contact the IDAES developers " "with this bug.".format(b.name)) b.add_component( cname + '_fw', Expression(b.component_list, rule=func_fw, doc='EoS S factor')) def func_a(m, j): cobj = m.params.get_component(j) fw = getattr(m, cname + "_fw") return (EoS_param[ctype]['omegaA'] * ((Cubic.gas_constant(b) * cobj.temperature_crit)**2 / cobj.pressure_crit) * ((1 + fw[j] * (1 - sqrt(m.temperature / cobj.temperature_crit)))**2)) b.add_component( cname + '_a', Expression(b.component_list, rule=func_a, doc='Component a coefficient')) def func_b(m, j): cobj = m.params.get_component(j) return (EoS_param[ctype]['coeff_b'] * Cubic.gas_constant(b) * cobj.temperature_crit / cobj.pressure_crit) b.add_component( cname + '_b', Expression(b.component_list, rule=func_b, doc='Component b coefficient')) def rule_am(m, p): try: rule = m.params.get_phase( p).config.equation_of_state_options["mixing_rule_a"] except KeyError: rule = MixingRuleA.default a = getattr(m, cname + "_a") if rule == MixingRuleA.default: return rule_am_default(m, cname, a, p) else: raise ConfigurationError( "{} Unrecognized option for Equation of State " "mixing_rule_a: {}. Must be an instance of MixingRuleA " "Enum.".format(m.name, rule)) b.add_component(cname + '_am', Expression(b.phase_list, rule=rule_am)) def rule_bm(m, p): try: rule = m.params.get_phase( p).config.equation_of_state_options["mixing_rule_b"] except KeyError: rule = MixingRuleB.default b = getattr(m, cname + "_b") if rule == MixingRuleB.default: return rule_bm_default(m, b, p) else: raise ConfigurationError( "{} Unrecognized option for Equation of State " "mixing_rule_a: {}. Must be an instance of MixingRuleB " "Enum.".format(m.name, rule)) b.add_component(cname + '_bm', Expression(b.phase_list, rule=rule_bm)) def rule_A(m, p): am = getattr(m, cname + "_am") return (am[p] * m.pressure / (Cubic.gas_constant(b) * m.temperature)**2) b.add_component(cname + '_A', Expression(b.phase_list, rule=rule_A)) def rule_B(m, p): bm = getattr(m, cname + "_bm") return (bm[p] * m.pressure / (Cubic.gas_constant(b) * m.temperature)) b.add_component(cname + '_B', Expression(b.phase_list, rule=rule_B)) def rule_delta(m, p, i): # See pg. 145 in Properties of Gases and Liquids a = getattr(m, cname + "_a") am = getattr(m, cname + "_am") kappa = getattr(m.params, cname + "_kappa") return (2 * sqrt(a[i]) / am[p] * sum(m.mole_frac_phase_comp[p, j] * sqrt(a[j]) * (1 - kappa[i, j]) for j in b.components_in_phase(p))) b.add_component(cname + "_delta", Expression(b.phase_component_set, rule=rule_delta)) def rule_dadT(m, p): # See pg. 102 in Properties of Gases and Liquids a = getattr(m, cname + "_a") fw = getattr(m, cname + "_fw") kappa = getattr(m.params, cname + "_kappa") return -( (Cubic.gas_constant(b) / 2) * sqrt(EoS_param[ctype]['omegaA']) * sum( sum(m.mole_frac_phase_comp[p, i] * m.mole_frac_phase_comp[p, j] * (1 - kappa[i, j]) * (fw[j] * sqrt( a[i] * m.params.get_component(j).temperature_crit / m.params.get_component(j).pressure_crit) + fw[i] * sqrt(a[j] * m.params.get_component(i).temperature_crit / m.params.get_component(i).pressure_crit)) for j in m.components_in_phase(p)) for i in m.components_in_phase(p)) / sqrt(m.temperature)) b.add_component(cname + "_dadT", Expression(b.phase_list, rule=rule_dadT)) # Add components at equilibrium state if required if (b.params.config.phases_in_equilibrium is not None and (not b.config.defined_state or b.always_flash)): def func_a_eq(m, p1, p2, j): cobj = m.params.get_component(j) fw = getattr(m, cname + "_fw") return ( EoS_param[ctype]['omegaA'] * ((Cubic.gas_constant(b) * cobj.temperature_crit)**2 / cobj.pressure_crit) * ((1 + fw[j] * (1 - sqrt(m._teq[p1, p2] / cobj.temperature_crit)))**2)) b.add_component( '_' + cname + '_a_eq', Expression(b.params._pe_pairs, b.component_list, rule=func_a_eq, doc='Component a coefficient at Teq')) def rule_am_eq(m, p1, p2, p3): try: rule = m.params.get_phase( p3).config.equation_of_state_options["mixing_rule_a"] except KeyError: rule = MixingRuleA.default a = getattr(m, "_" + cname + "_a_eq") if rule == MixingRuleA.default: return rule_am_default(m, cname, a, p3, (p1, p2)) else: raise ConfigurationError( "{} Unrecognized option for Equation of State " "mixing_rule_a: {}. Must be an instance of MixingRuleA " "Enum.".format(m.name, rule)) b.add_component( '_' + cname + '_am_eq', Expression(b.params._pe_pairs, b.phase_list, rule=rule_am_eq)) def rule_A_eq(m, p1, p2, p3): am_eq = getattr(m, "_" + cname + "_am_eq") return (am_eq[p1, p2, p3] * m.pressure / (Cubic.gas_constant(b) * m._teq[p1, p2])**2) b.add_component( '_' + cname + '_A_eq', Expression(b.params._pe_pairs, b.phase_list, rule=rule_A_eq)) def rule_B_eq(m, p1, p2, p3): bm = getattr(m, cname + "_bm") return (bm[p3] * m.pressure / (Cubic.gas_constant(b) * m._teq[p1, p2])) b.add_component( '_' + cname + '_B_eq', Expression(b.params._pe_pairs, b.phase_list, rule=rule_B_eq)) def rule_delta_eq(m, p1, p2, p3, i): # See pg. 145 in Properties of Gases and Liquids a = getattr(m, "_" + cname + "_a_eq") am = getattr(m, "_" + cname + "_am_eq") kappa = getattr(m.params, cname + "_kappa") return ( 2 * sqrt(a[p1, p2, i]) / am[p1, p2, p3] * sum(m.mole_frac_phase_comp[p3, j] * sqrt(a[p1, p2, j]) * (1 - kappa[i, j]) for j in m.components_in_phase(p3))) b.add_component( "_" + cname + "_delta_eq", Expression(b.params._pe_pairs, b.phase_component_set, rule=rule_delta_eq)) # Set up external function calls b.add_component("_" + cname + "_ext_func_param", Param(default=ctype.value)) b.add_component("_" + cname + "_proc_Z_liq", ExternalFunction(library=_so, function="ceos_z_liq")) b.add_component("_" + cname + "_proc_Z_vap", ExternalFunction(library=_so, function="ceos_z_vap"))