@@ -1488,6 +1488,120 @@ def test_set_initial_conditions_symbol_with_value_attribute(self):
14881488 disc_var2 = next (iter (model2_disc .initial_conditions .keys ()))
14891489 assert isinstance (model2_disc .initial_conditions [disc_var2 ], pybamm .Vector )
14901490
1491+ def test_build_initial_state_mapper_reuses_input_value (self ):
1492+ # ``from_model``: only ``s`` is a state. ``current`` is exposed as a
1493+ # named variable whose value comes from an InputParameter.
1494+ s_from = pybamm .Variable ("s" )
1495+ from_model = pybamm .BaseModel ()
1496+ from_model .rhs = {s_from : - s_from }
1497+ from_model .initial_conditions = {s_from : pybamm .Scalar (0.5 )}
1498+ from_model .variables = {
1499+ "s" : s_from ,
1500+ "current" : pybamm .InputParameter ("I_in" ),
1501+ }
1502+ pybamm .Discretisation ().process_model (from_model )
1503+
1504+ # ``to_model``: ``current`` is now an algebraic state with a *wrong*
1505+ # initial-condition guess of 0 (must NOT be used by the mapper).
1506+ s_to = pybamm .Variable ("s" )
1507+ c_to = pybamm .Variable ("current" )
1508+ to_model = pybamm .BaseModel ()
1509+ to_model .rhs = {s_to : - s_to }
1510+ to_model .algebraic = {c_to : c_to - pybamm .Scalar (1.0 )}
1511+ to_model .initial_conditions = {
1512+ s_to : pybamm .Scalar (0.0 ),
1513+ c_to : pybamm .Scalar (0.0 ),
1514+ }
1515+ to_model .variables = {"s" : s_to , "current" : c_to }
1516+ pybamm .Discretisation ().process_model (to_model )
1517+
1518+ mapper = to_model .build_initial_state_mapper (from_model )
1519+
1520+ # Evaluate the mapper with a known previous state vector and input.
1521+ y_from = np .array ([[0.7 ]])
1522+ result = np .asarray (
1523+ mapper .evaluate (t = 0 , y = y_from , inputs = {"I_in" : 3.0 })
1524+ ).ravel ()
1525+
1526+ # Map (s, current) onto y_slices ordering of the target model.
1527+ s_slice = to_model .y_slices [s_to ][0 ]
1528+ c_slice = to_model .y_slices [c_to ][0 ]
1529+ assert result [s_slice ][0 ] == pytest .approx (0.7 )
1530+ # Critical: must equal the previous step's input, not the target IC (0).
1531+ assert result [c_slice ][0 ] == pytest .approx (3.0 )
1532+
1533+ def test_build_initial_state_mapper_handles_scale_and_reference (self ):
1534+ from_scale_s = pybamm .Scalar (2.0 )
1535+ from_ref_s = pybamm .Scalar (10.0 )
1536+ s_from = pybamm .Variable ("s" , scale = from_scale_s , reference = from_ref_s )
1537+ from_model = pybamm .BaseModel ()
1538+ from_model .rhs = {s_from : - s_from }
1539+ from_model .initial_conditions = {s_from : pybamm .Scalar (0.0 )}
1540+ from_model .variables = {
1541+ "s" : s_from ,
1542+ "current" : pybamm .InputParameter ("I_in" ),
1543+ }
1544+ pybamm .Discretisation ().process_model (from_model )
1545+
1546+ to_scale_s = pybamm .Scalar (4.0 )
1547+ to_ref_s = pybamm .Scalar (- 5.0 )
1548+ to_scale_c = pybamm .Scalar (0.5 )
1549+ to_ref_c = pybamm .Scalar (100.0 )
1550+ s_to = pybamm .Variable ("s" , scale = to_scale_s , reference = to_ref_s )
1551+ c_to = pybamm .Variable ("current" , scale = to_scale_c , reference = to_ref_c )
1552+ to_model = pybamm .BaseModel ()
1553+ to_model .rhs = {s_to : - s_to }
1554+ to_model .algebraic = {c_to : c_to - pybamm .Scalar (1.0 )}
1555+ to_model .initial_conditions = {
1556+ s_to : pybamm .Scalar (0.0 ),
1557+ c_to : pybamm .Scalar (0.0 ),
1558+ }
1559+ to_model .variables = {"s" : s_to , "current" : c_to }
1560+ pybamm .Discretisation ().process_model (to_model )
1561+
1562+ mapper = to_model .build_initial_state_mapper (from_model )
1563+
1564+ # Choose physical values, encode via from_model scaling, decode in mapper.
1565+ phys_s = 17.0
1566+ y_s_from = (phys_s - 10.0 ) / 2.0 # 3.5
1567+ phys_current = 6.25
1568+ y_from = np .array ([[y_s_from ]])
1569+ result = np .asarray (
1570+ mapper .evaluate (t = 0 , y = y_from , inputs = {"I_in" : phys_current })
1571+ ).ravel ()
1572+
1573+ s_slice = to_model .y_slices [s_to ][0 ]
1574+ c_slice = to_model .y_slices [c_to ][0 ]
1575+ # s: matched-state branch must round-trip through physical units.
1576+ # expected = (17 - (-5)) / 4 = 5.5
1577+ assert result [s_slice ][0 ] == pytest .approx ((phys_s - (- 5.0 )) / 4.0 )
1578+ # current: input-parameter branch.
1579+ # expected = (6.25 - 100) / 0.5 = -187.5
1580+ assert result [c_slice ][0 ] == pytest .approx ((phys_current - 100.0 ) / 0.5 )
1581+
1582+ def test_build_initial_state_mapper_missing_variable_raises (self ):
1583+ s_from = pybamm .Variable ("s" )
1584+ from_model = pybamm .BaseModel ()
1585+ from_model .rhs = {s_from : - s_from }
1586+ from_model .initial_conditions = {s_from : pybamm .Scalar (0.5 )}
1587+ from_model .variables = {"s" : s_from }
1588+ pybamm .Discretisation ().process_model (from_model )
1589+
1590+ s_to = pybamm .Variable ("s" )
1591+ new_var = pybamm .Variable ("brand_new" )
1592+ to_model = pybamm .BaseModel ()
1593+ to_model .rhs = {s_to : - s_to }
1594+ to_model .algebraic = {new_var : new_var - pybamm .Scalar (1.0 )}
1595+ to_model .initial_conditions = {
1596+ s_to : pybamm .Scalar (0.0 ),
1597+ new_var : pybamm .Scalar (0.0 ),
1598+ }
1599+ to_model .variables = {"s" : s_to , "brand_new" : new_var }
1600+ pybamm .Discretisation ().process_model (to_model )
1601+
1602+ with pytest .raises (pybamm .ModelError , match = "brand_new" ):
1603+ to_model .build_initial_state_mapper (from_model )
1604+
14911605 def test_set_variables_error (self ):
14921606 var = pybamm .Variable ("var" )
14931607 model = pybamm .BaseModel ()
0 commit comments