1+ # Required import for python
2+ import Sofa
3+ import numpy as np
4+
5+
6+ def main ():
7+ import SofaRuntime
8+ import Sofa .Gui
9+ import SofaImGui
10+
11+ root = Sofa .Core .Node ("root" )
12+ createScene (root )
13+ Sofa .Simulation .init (root )
14+
15+ Sofa .Gui .GUIManager .Init ("myscene" , "imgui" )
16+ Sofa .Gui .GUIManager .createGUI (root , __file__ )
17+ Sofa .Gui .GUIManager .SetDimension (1080 , 600 )
18+ Sofa .Gui .GUIManager .MainLoop (root )
19+ Sofa .Gui .GUIManager .closeGUI ()
20+
21+
22+ def createScene (root ):
23+ root .gravity = [0 , - 9.81 , 0 ]
24+ root .dt = 0.01
25+ root .bbox = [[- 0.5 , - 0.5 , - 0.5 ], [0.5 , 0.5 , 0.5 ]]
26+
27+ root .addObject ('DefaultAnimationLoop' )
28+ root .addObject ('DefaultVisualManagerLoop' )
29+ root .addObject ('RequiredPlugin' , pluginName = ['Sofa.Component.Constraint.Projective' , 'Sofa.Component.Diffusion' , 'Sofa.Component.Engine.Select' ,
30+ 'Sofa.Component.LinearSolver.Direct' , 'Sofa.Component.LinearSolver.Iterative' , 'Sofa.Component.Mass' , 'Sofa.Component.ODESolver.Backward' , 'Sofa.Component.SolidMechanics.FEM.Elastic' ,
31+ 'Sofa.Component.Topology.Container.Dynamic' , 'Sofa.Component.Topology.Container.Grid' , 'Sofa.Component.Topology.Mapping' , 'Sofa.Component.Visual' , 'Sofa.GL.Component.Engine' , 'Sofa.GL.Component.Rendering2D' ,
32+ 'Sofa.GL.Component.Rendering3D' , 'Sofa.Component.StateContainer' , 'Sofa.Component.Mapping.Linear' ])
33+ root .addObject ('VisualStyle' , displayFlags = "hideCollisionModels showVisualModels hideForceFields showBehaviorModels" )
34+
35+ gridGenerator = root .addObject ('RegularGridTopology' , name = "gridGenerator" , nx = 16 , ny = 6 , nz = 6 , xmin = 0 , xmax = 1 , ymin = 0 , ymax = 0.2 , zmin = 0 , zmax = 0.2 )
36+ root .addObject ("OglColorMap" , legendTitle = "A-dimensional temperature" , legendOffset = [60 ,0 ], legendSize = 20 , min = 0 , max = 1 , showLegend = True , colorScheme = "Blue to Red" )
37+
38+
39+ tetraTopo = root .addChild ('TetraTopology' )
40+ tetrahedraContainer = tetraTopo .addObject ("TetrahedronSetTopologyContainer" , name = "tetContainer" , tags = "3dgeometry" )
41+ tetraTopo .addObject ("TetrahedronSetTopologyModifier" , name = "Modifier" )
42+ tetraTopo .addObject ("Hexa2TetraTopologicalMapping" , input = gridGenerator .linkpath , output = tetrahedraContainer .linkpath )
43+ tetraTopo .addObject ("MechanicalObject" , name = "tetraO" , position = "@../gridGenerator.position" , tags = "3dgeometry" )
44+ tetraTopo .addObject ("BoxROI" , name = "BoundaryCondition" , box = [- 0.01 , - 0.01 , - 0.01 , 0.01 , 0.21 , 0.21 ])
45+
46+
47+ meca = tetraTopo .addChild ("Mechanics" )
48+ meca .addObject ("EulerImplicitSolver" , name = "Euler Impl IntegrationScheme" )
49+ meca .addObject ("SparseLDLSolver" , name = "LDL LinearSolver" , template = "CompressedRowSparseMatrixMat3x3d" )
50+ meca .addObject ("TetrahedronSetTopologyContainer" , name = "tetContainer" , src = tetrahedraContainer .linkpath )
51+ meca .addObject ("TetrahedronSetGeometryAlgorithms" , name = "tetGeometry" , template = "Vec3d" )
52+ elasticMO = meca .addObject ("MechanicalObject" , name = "MO" , position = "@../tetraO.position" )
53+ meca .addObject ("MeshMatrixMass" , template = "Vec3d,Vec3d" , name = "Mass" , massDensity = 100 , topology = tetrahedraContainer .linkpath , geometryState = "@MO" )
54+ meca .addObject ("FixedConstraint" , name = "FixedBoundaryCondition" , indices = "@../BoundaryCondition.indices" )
55+
56+ #initialization of the vector multiplying the local Young's modulus (need to activate updateStiffness=True)
57+ initVector = np .full ((1 , 576 ), 1.0 )
58+ meca .addObject ("TetrahedronFEMForceField" , name = "LinearElasticity" , youngModulus = 3e5 , poissonRatio = 0.4 , computeVonMisesStress = 1 , showVonMisesStressPerElement = True , localStiffnessFactor = initVector , updateStiffness = True )
59+
60+
61+ thermo = tetraTopo .addChild ("Thermodynamics" )
62+ thermo .addObject ("EulerImplicitSolver" , name = "Euler Impl IntegrationScheme" , firstOrder = True )
63+ thermo .addObject ("CGLinearSolver" , name = "Conjugate Gradient" , iterations = "1000" , tolerance = 1.0e-10 , threshold = 1.0e-30 )
64+ thermo .addObject ("TetrahedronSetTopologyContainer" , name = "tetContainer" , src = "@../tetContainer" )
65+ thermo .addObject ("TetrahedronSetGeometryAlgorithms" , name = "tetGeometry" , template = "Vec3d" )
66+ thermo .addObject ("MechanicalObject" , name = "Temperatures" , template = "Vec1d" , size = 576 , showObject = True )
67+ thermo .addObject ("MeshMatrixMass" , template = "Vec1d,Vec3d" , name = "Conductivity" , massDensity = 1.0 , topology = tetrahedraContainer .linkpath , geometryState = elasticMO .linkpath )
68+ thermo .addObject ("FixedConstraint" , name = "Heating" , indices = 495 )
69+ thermo .addObject ("TetrahedronDiffusionFEMForceField" , name = "DiffusionForceField" , template = "Vec1d" , constantDiffusionCoefficient = 0.05 , tagMechanics = "3dgeometry" , mstate = "@Temperatures" )
70+
71+ thermoVisu = thermo .addChild ("Visu" )
72+ thermoVisu .addObject ("TextureInterpolation" , template = "Vec1d" , name = "EngineInterpolation" , input_states = "@../Temperatures.position" , input_coordinates = "@../../Mechanics/MO.position" , min_value = 0. , max_value = 1. , manual_scale = True , drawPotentiels = False , showIndicesScale = 5e-05 )
73+ thermoVisu .addObject ("OglModel" , template = "Vec3d" , name = "oglPotentiel" , handleDynamicTopology = "0" , texcoords = "@EngineInterpolation.output_coordinates" ,texturename = "textures/heatColor.bmp" , scale3d = [1 , 1 , 1 ], material = "Default Diffuse 1 1 1 1 0.5 Ambient 1 1 1 1 0.3 Specular 0 0.5 0.5 0.5 1 Emissive 0 0.5 0.5 0.5 1 Shininess 0 45 No texture linked to the material No bump texture linked to the material " )
74+ thermoVisu .addObject ("IdentityMapping" , input = elasticMO .linkpath , output = "@oglPotentiel" )
75+
76+ # Add the controller
77+ root .addObject (ThermoMecaController (name = "MultiPhysicsController" , ThermalObject = thermo .Temperatures , MechanicalForceField = meca .LinearElasticity ))
78+
79+ return root
80+
81+
82+ class ThermoMecaController (Sofa .Core .Controller ):
83+ def __init__ (self , * args , ** kwargs ):
84+ # These are needed (and the normal way to override from a python class)
85+ Sofa .Core .Controller .__init__ (self , * args , ** kwargs )
86+ # Recover the pointers to the thermal object and the ForceField responsible of the linear elastic constitutive law
87+ self .ThermalObject = kwargs .get ("ThermalObject" )
88+ self .MechanicalForceField = kwargs .get ("MechanicalForceField" )
89+
90+ def onAnimateEndEvent (self , event ):
91+ time = self .getContext ().getTime ()
92+
93+ self .temperatures = self .ThermalObject .position .array ()
94+
95+ if time > 0.5 :
96+
97+ # Make the Young's modulus related to the temperature: E = E_init / (1 + 50*Temperature)
98+ with self .MechanicalForceField .localStiffnessFactor .writeableArray () as wa :
99+ for i in range (576 ) :
100+ wa [i ] = 1.0 / ( 1. + 50. * self .temperatures [i ][0 ] )
101+
102+ #Enforce temperature of the node 495 (extremity of the bar)
103+ # Heating phase until t = 4
104+ if time < 4.0 :
105+ with self .ThermalObject .position .writeableArray () as wt :
106+ wt [495 ][0 ] = 1.0
107+ # Cooling down phase when t > 4
108+ else :
109+ with self .ThermalObject .position .writeableArray () as wt :
110+ wt [495 ][0 ] = 0.0
111+
112+
113+ # Function used only if this script is called from a python environment
114+ if __name__ == '__main__' :
115+ main ()
0 commit comments