8080 ("spinningBodiesOneDOF" , True ),
8181 ("spinningBodiesTwoDOF" , True ),
8282 ("spinningBodiesNDOF" , True ),
83- # ("linearTranslationBodiesOneDOF", True),
83+ ("linearTranslationBodiesOneDOF" , True ),
8484 # ("linearTranslationBodiesNDOF", True),
8585 ("linearSpringMassDamper" , False ),
8686 # ("sphericalPendulum", False),
@@ -117,17 +117,8 @@ def test_effectorBranchingIntegratedTest(show_plots, stateEffector, isParent, dy
117117 addDynamicEffector() method is called. The dynamic effector compatibility is checked at
118118 simulation initialization when linkInProperties() is called.
119119
120- State effectors that are expected to be able to host dynamic effectors (isParent) include:
121-
122- - :ref:`spinningBodyOneDOFStateEffector`
123- - :ref:`spinningBodyTwoDOFStateEffector`
124- - :ref:`spinningBodyNDOFStateEffector`
125-
126- Dynamic effectors that are expected to be able to attach to state effectors (isChild) include:
127-
128- - :ref:`extForceTorque`
129- - :ref:`thrusterDynamicEffector`
130- - :ref:`constraintDynamicEffector`
120+ A summary of which state effectors and dynamic effectors are expected to be able to act as a
121+ parent or child effector to another is summarized in :ref:`bskPrinciples-11`.
131122
132123 Note that the constraint effector is tested in two configurations: once where one vehicle's
133124 state effector is attached to the hub on another vehicle (``constraintEffectorOneHub``), and
@@ -259,6 +250,9 @@ def effectorBranchingIntegratedTest(show_plots, stateEffector, isParent, dynamic
259250 elif stateEffector == "spinningBodiesNDOF" :
260251 stateEff , stateEffProps = setup_spinningBodiesNDOF ()
261252 segment = 4
253+ elif stateEffector == "linearTranslationBodiesOneDOF" :
254+ stateEff , stateEffProps = setup_translatingBodiesOneDOF ()
255+ segment = 1
262256 elif stateEffector == "linearSpringMassDamper" :
263257 stateEff , stateEffProps = setup_linearSpringMassDamper ()
264258 segment = 1
@@ -340,13 +334,27 @@ def effectorBranchingIntegratedTest(show_plots, stateEffector, isParent, dynamic
340334 assert isChild , "FAILED: attached an incompatible dynamic effector without erroring"
341335
342336 # Check that properties are being handed correctly from state effector to dynamic effector
343- assert getDynEffInertialPropName (dynamicEffector , dynamicEff , "Position" ) == getStateEffInertialPropName (scObject , segment , stateEff , "Position" ), (
337+ if (stateEffector == "spinningBodiesNDOF" or stateEffector == "linearTranslationBodiesOneDOF"
338+ or stateEffector == "linearTranslationBodiesNDOF" ):
339+ # Newer effector classes keep all variables private and so we check with the dynParamManager
340+ positionName = getModernStateEffInertialPropName (scObject , segment , stateEff , "Position" )
341+ velocityName = getModernStateEffInertialPropName (scObject , segment , stateEff , "Velocity" )
342+ attitudeName = getModernStateEffInertialPropName (scObject , segment , stateEff , "Attitude" )
343+ angvelocityName = getModernStateEffInertialPropName (scObject , segment , stateEff , "AngVelocity" )
344+ else :
345+ # older effector classes have public variable names that are simply checked directly
346+ positionName = getStateEffInertialPropName (segment , stateEff , "Position" )
347+ velocityName = getStateEffInertialPropName (segment , stateEff , "Velocity" )
348+ attitudeName = getStateEffInertialPropName (segment , stateEff , "Attitude" )
349+ angvelocityName = getStateEffInertialPropName (segment , stateEff , "AngVelocity" )
350+
351+ assert getDynEffInertialPropName (dynamicEffector , dynamicEff , "Position" ) == positionName , (
344352 "FAILED: inertialPositionProperty not handed correctly between state and dynamic effectors" )
345- assert getDynEffInertialPropName (dynamicEffector , dynamicEff , "Velocity" ) == getStateEffInertialPropName ( scObject , segment , stateEff , "Velocity" ) , (
353+ assert getDynEffInertialPropName (dynamicEffector , dynamicEff , "Velocity" ) == velocityName , (
346354 "FAILED: inertialVelocityProperty not handed correctly between state and dynamic effectors" )
347- assert getDynEffInertialPropName (dynamicEffector , dynamicEff , "Attitude" ) == getStateEffInertialPropName ( scObject , segment , stateEff , "Attitude" ) , (
355+ assert getDynEffInertialPropName (dynamicEffector , dynamicEff , "Attitude" ) == attitudeName , (
348356 "FAILED: inertialAttitudeProperty not handed correctly between state and dynamic effectors" )
349- assert getDynEffInertialPropName (dynamicEffector , dynamicEff , "AngVelocity" ) == getStateEffInertialPropName ( scObject , segment , stateEff , "AngVelocity" ) , (
357+ assert getDynEffInertialPropName (dynamicEffector , dynamicEff , "AngVelocity" ) == angvelocityName , (
350358 "FAILED: inertialAngVelocityProperty not handed correctly between state and dynamic effectors" )
351359
352360 # Run the sim for a few timesteps to confirm execution without error
@@ -379,11 +387,18 @@ def effectorBranchingIntegratedTest(show_plots, stateEffector, isParent, dynamic
379387 dV [idx ,:] = (dV [idx - 1 ,:] + (dcm_NS @ np .array (dynamicEff .extForce_B ).flatten ())
380388 / (scObject .hub .mHub + stateEffProps .totalMass ) * timestep )
381389 # Compute the total external torque on the vehicle
382- extTorque [idx ,:] = (dcm_NS @ np .array (dynamicEff .extTorquePntB_B ).flatten ()
383- + np .cross (r_ScN_N_log [idx + 1 ,:] - dcm_NS
384- @ np .array (stateEffProps .r_PcP_P ).flatten ()
385- - datLog .r_CN_N [idx ,:], dcm_NS
386- @ np .array (dynamicEff .extForce_B ).flatten ()))
390+ if stateEffector == "linearTranslationBodiesOneDOF" or stateEffector == "linearTranslationBodiesNDOF" :
391+ extTorque [idx ,:] = (dcm_NS @ np .array (dynamicEff .extTorquePntB_B ).flatten ()
392+ + np .cross (r_ScN_N_log [idx + 1 ,:] - dcm_NS
393+ @ np .array (stateEffProps .r_PcP_P ).flatten ()
394+ - datLog .r_CN_N [idx ,:], dcm_NS
395+ @ np .array (dynamicEff .extForce_B ).flatten ()))
396+ else :
397+ extTorque [idx ,:] = (dcm_NS @ np .array (dynamicEff .extTorquePntB_B ).flatten ()
398+ + np .cross (r_ScN_N_log [idx + 1 ,:] - dcm_NS
399+ @ np .array (stateEffProps .r_PcP_P ).flatten ()
400+ - datLog .r_CN_N [idx ,:], dcm_NS
401+ @ np .array (dynamicEff .extForce_B ).flatten ()))
387402
388403 # Integrate the torque to find accumulated change in angular momentum
389404 dx = np .ones (n - 1 )* timestep
@@ -437,12 +452,16 @@ def effectorBranchingIntegratedTest(show_plots, stateEffector, isParent, dynamic
437452
438453 # Check angular momentum difference against sim truth
439454 angMom_accuracy = 1e-3
440- np .testing .assert_allclose (rotAngMom_N [:- 1 ,:]- rotAngMom_N [0 ,:] - dH , 0 , atol = angMom_accuracy ,
455+ # np.testing.assert_allclose(np.linalg.norm(rotAngMom_N[:-1,:]-rotAngMom_N[0,:] - dH, axis=1), 0, atol=angMom_accuracy,
456+ # err_msg="angular momentum difference beyond accuracy limits")
457+ np .testing .assert_allclose (rotAngMom_N [:- 1 ,:]- rotAngMom_N [0 ,:], dH , atol = angMom_accuracy ,
441458 err_msg = "angular momentum difference beyond accuracy limits" )
442459
443460 # Check deltaV difference against sim truth
444461 deltaV_accuracy = 1e-6
445- np .testing .assert_allclose (totAccumDV_N [:- 1 ,:] - dV , 0 , atol = deltaV_accuracy ,
462+ # np.testing.assert_allclose(np.linalg.norm(totAccumDV_N[:-1,:] - dV, axis=1), 0, atol=deltaV_accuracy,
463+ # err_msg="deltaV difference beyond accuracy limits")
464+ np .testing .assert_allclose (totAccumDV_N [:- 1 ,:], dV , 0 , atol = deltaV_accuracy ,
446465 err_msg = "deltaV difference beyond accuracy limits" )
447466
448467 return
@@ -456,7 +475,7 @@ def getDynEffInertialPropName(dynamicEffector, dynamicEff, propType):
456475 else :
457476 return getattr (dynamicEff , f"getPropName_inertial{ propType } " )()
458477
459- def getStateEffInertialPropName (scObject , segment , stateEff , propType ):
478+ def getStateEffInertialPropName (segment , stateEff , propType ):
460479 if segment == 1 :
461480 return getattr (stateEff , f"nameOfInertial{ propType } Property" )
462481 elif segment == 2 :
@@ -469,6 +488,18 @@ def getStateEffInertialPropName(scObject, segment, stateEff, propType):
469488 return "notHandedCorrectly"
470489 return propName
471490
491+ def getModernStateEffInertialPropName (scObject , segment , stateEff , propType ):
492+ try :
493+ if segment == 1 :
494+ propName = stateEff .ModelTag + "Inertial" + propType + "1"
495+ scObject .dynManager .getPropertyReference (propName )
496+ elif segment == 4 :
497+ propName = stateEff .ModelTag + "Inertial" + propType + "1_4"
498+ scObject .dynManager .getPropertyReference (propName )
499+ except BasiliskError :
500+ return "notHandedCorrectly"
501+ return propName
502+
472503def setup_extForceTorque ():
473504 extFT = extForceTorque .ExtForceTorque ()
474505 extFT .extForce_B = [[1.0 ], [1.0 ], [1.0 ]]
@@ -621,7 +652,7 @@ def setup_spinningBodiesOneDOF():
621652 # Compute COM offset contribution, to be divided by the hub mass
622653 mr_ScB_B = - (spinningBody .r_SB_B + np .transpose (spinningBody .dcm_S0B ) @ spinningBody .r_ScS_S ) * spinningBody .mass
623654
624- stateEffProps = stateEfectorProperties ()
655+ stateEffProps = stateEffectorProperties ()
625656 stateEffProps .totalMass = spinningBody .mass
626657 stateEffProps .mr_PcB_B = mr_ScB_B
627658 stateEffProps .r_PB_B = spinningBody .r_SB_B
@@ -640,10 +671,10 @@ def setup_spinningBodiesTwoDOF():
640671 spinningBody .IS2PntSc2_S2 = [[50.0 , 0.0 , 0.0 ], [0.0 , 30.0 , 0.0 ], [0.0 , 0.0 , 40.0 ]]
641672 spinningBody .dcm_S10B = [[- 1.0 , 0.0 , 0.0 ], [0.0 , - 1.0 , 0.0 ], [0.0 , 0.0 , 1.0 ]]
642673 spinningBody .dcm_S20S1 = [[0.0 , - 1.0 , 0.0 ], [0.0 , .0 , - 1.0 ], [1.0 , 0.0 , 0.0 ]]
643- spinningBody .r_Sc1S1_S1 = [[2 .0 ], [- 0.5 ], [0.0 ]]
674+ spinningBody .r_Sc1S1_S1 = [[1 .0 ], [- 0.5 ], [0.0 ]]
644675 spinningBody .r_Sc2S2_S2 = [[1.0 ], [0.0 ], [- 1.0 ]]
645- spinningBody .r_S1B_B = [[- 2 .0 ], [0.5 ], [- 1.0 ]]
646- spinningBody .r_S2S1_S1 = [[0.5 ], [- 1 .5 ], [- 0.5 ]]
676+ spinningBody .r_S1B_B = [[- 1 .0 ], [0.5 ], [- 1.0 ]]
677+ spinningBody .r_S2S1_S1 = [[0.5 ], [- 0 .5 ], [- 0.5 ]]
647678 spinningBody .s1Hat_S1 = [[0 ], [0 ], [1 ]]
648679 spinningBody .s2Hat_S2 = [[0 ], [- 1 ], [0 ]]
649680 spinningBody .theta1DotInit = 1.0 * macros .D2R
@@ -661,7 +692,7 @@ def setup_spinningBodiesTwoDOF():
661692 np .transpose (spinningBody .dcm_S20S1 ) @ spinningBody .r_Sc2S2_S2 ) )
662693 * spinningBody .mass2 )
663694
664- stateEffProps = stateEfectorProperties ()
695+ stateEffProps = stateEffectorProperties ()
665696 stateEffProps .totalMass = spinningBody .mass1 + spinningBody .mass2
666697 stateEffProps .mr_PcB_B = mr_ScB_B
667698 stateEffProps .r_PB_B = spinningBody .r_S1B_B + np .transpose (spinningBody .dcm_S10B ) @ spinningBody .r_S2S1_S1
@@ -729,7 +760,7 @@ def setup_spinningBodiesNDOF():
729760
730761 spinningBodyEffector .ModelTag = "spinningBody"
731762
732- stateEffProps = stateEfectorProperties ()
763+ stateEffProps = stateEffectorProperties ()
733764 stateEffProps .totalMass = massSubPanel * numberOfSegments
734765 stateEffProps .mr_PcB_B = mr_ScB_B
735766 stateEffProps .r_PB_B = r_ScB_B - dcm_SB .transpose () @ spinningBody .getR_ScS_S ()
@@ -738,6 +769,39 @@ def setup_spinningBodiesNDOF():
738769
739770 return (spinningBodyEffector , stateEffProps )
740771
772+ def setup_translatingBodiesOneDOF ():
773+ translatingBody = linearTranslationOneDOFStateEffector .LinearTranslationOneDOFStateEffector ()
774+
775+ # Define properties of translating body
776+ translatingBody .setMass (20.0 )
777+ translatingBody .setK (100.0 )
778+ translatingBody .setC (50.0 )
779+ translatingBody .setRhoInit (1.0 )
780+ translatingBody .setRhoDotInit (0.05 )
781+ translatingBody .setFHat_B ([[3.0 / 5.0 ], [4.0 / 5.0 ], [0.0 ]])
782+ translatingBody .setR_FcF_F ([[- 1.0 ], [1.0 ], [0.0 ]])
783+ translatingBody .setR_F0B_B ([[- 1.0 ], [1.0 ], [0.0 ]])
784+ translatingBody .setIPntFc_F ([[50.0 , 0.0 , 0.0 ],
785+ [0.0 , 80.0 , 0.0 ],
786+ [0.0 , 0.0 , 60.0 ]])
787+ translatingBody .setDCM_FB ([[0.0 , - 1.0 , 0.0 ],
788+ [0.0 , 0.0 , - 1.0 ],
789+ [1.0 , 0.0 , 0.0 ]])
790+ translatingBody .ModelTag = "linearTranslation"
791+
792+ mr_ScB_B = - (translatingBody .getR_F0B_B () + np .transpose (translatingBody .getDCM_FB ()) @
793+ (translatingBody .getR_FcF_F () + translatingBody .getRhoInit () *
794+ np .array (translatingBody .getFHat_B ()))) * translatingBody .getMass ()
795+
796+ stateEffProps = stateEffectorProperties ()
797+ stateEffProps .totalMass = translatingBody .getMass ()
798+ stateEffProps .mr_PcB_B = mr_ScB_B
799+ stateEffProps .r_PB_B = translatingBody .getR_F0B_B ()
800+ stateEffProps .r_PcP_P = translatingBody .getR_FcF_F ()
801+ stateEffProps .inertialPropLogName = "translatingBodyConfigLogOutMsg"
802+
803+ return (translatingBody , stateEffProps )
804+
741805def setup_linearSpringMassDamper ():
742806 linearSpring = linearSpringMassDamper .LinearSpringMassDamper ()
743807 linearSpring .massInit = 50.0
@@ -752,14 +816,14 @@ def setup_linearSpringMassDamper():
752816 # Compute COM offset contribution, to be divided by the hub mass
753817 mr_ScB_B = - (linearSpring .r_PB_B + linearSpring .rhoInit * np .array (linearSpring .pHat_B )) * linearSpring .massInit
754818
755- stateEffProps = stateEfectorProperties ()
819+ stateEffProps = stateEffectorProperties ()
756820 stateEffProps .totalMass = linearSpring .massInit
757821 stateEffProps .mr_PcB_B = mr_ScB_B
758822 stateEffProps .r_PB_B = linearSpring .r_PB_B
759823
760824 return (linearSpring , stateEffProps )
761825
762- class stateEfectorProperties :
826+ class stateEffectorProperties :
763827 # to be used in joint COM calculation
764828 totalMass = 0.0 # total mass of the effector (sum of all linkages)
765829 mr_PcB_B = [[0.0 ], [0.0 ], [0.0 ]] # sum(m_i * r_SiB_B) for i linkages, see rst documentation
@@ -769,4 +833,4 @@ class stateEfectorProperties:
769833 r_PcP_P = [[0.0 ], [0.0 ], [0.0 ]] # individual COM for linkage that dynEff will be attached to
770834
771835if __name__ == "__main__" :
772- effectorBranchingIntegratedTest (True , "spinningBodiesOneDOF " , True , "extForceTorque" , True )
836+ effectorBranchingIntegratedTest (True , "linearTranslationBodiesOneDOF " , True , "extForceTorque" , True )
0 commit comments