How To: Simulation Results & Queries

  • Create a QueryResult object that specifies the domain to be sampled. It takes a single component or an assembly as input.

    assembly = [component_1, component_2]
    
    results = QueryResult(component_1) # sample only the defined component
    results = QueryResult(assembly) # sample on the full assembly
    
  • Specify a query class and type that defines what quantity you are querying. The query classes and types available are as follows :

    • Global Query is for querying quantities defined for the entire domain or structure. The available GlobalQueryTypes are Compliance, ReactionForce, TotalReactonForce, TotalReactionMoment, TotalAppliedForce, and TotalAppliedMoment for linear elasticity simulations, Frequency for modal simulations, CriticalLoadFactor for linear buckling simulations, and ThermalCompliance for static thermal simulations.

      TotalReactionForce is a tuple of dimension 3 containing the total reaction force

      [x-force, y-force, z-force]
      

      ReactionForce is a tuple of dimension 3 for each restrained boundary, in the order the boundary conditions were added to the LinearElasticScenarioDescriptor.

      [x-force, y-force, z-force]
      

      TotalReactionMoment is 3 tuples of dimension 3, representing the moments about the origin generated by reaction forces. Using [r1, r2, r3] to represent the vector from the origin to the reaction force, and using [Fx, Fy, Fz] to represent the reaction force, the moments are:

      [0, r2 * Fx, r3 * Fx]
      [r1 * Fy, 0, r3 * Fy]
      [r1 * Fz, r2 * Fz, 0]
      

      TotalAppliedForce is a tuple of dimension 3, representing the total applied load.

      [Fx, Fy, Fz]
      

      TotalAppliedMoment is 3 tuples of dimension 3, representing the moments about the origin generated by the applied loads. Using [r1, r2, r3] to represent the vector from the origin to the applied load, and using [Fx, Fy, Fz] to represent the applied load, the moments are:

      [0, r2 * Fx, r3 * Fx]
      [r1 * Fy, 0, r3 * Fy]
      [r1 * Fz, r2 * Fz, 0]
      
    # GlobalQuery example
    # Create a GlobalQuery to query the first mode
    results = QueryResult(assembly)
    frequency_query = GlobalQuery(GlobalQueryType.Frequency, DiscreteIndex(0))
    
    # Sample the simulation for the specified query
    freq_1 = simulator.sample(mode1_query, results)
    print(freq_1.get(0,0)) # Print out the first mode in Hz
    
    # Example use to get the first 10 modes
    # Note, the simulator would need this:
    # modal_scenario.modal_metadata.desired_eigenvalues = 10
    
    for i in range(10):
        # Create the query for the current mode
        query = GlobalQuery(GlobalQueryType.Frequency, DiscreteIndex(i))
    
        # Sample the query using the simulator
        r = simulator.sample(query, results)
    
        # Append the value obtained from result.get(0, 0) to a list
        values.append(r.get(0, 0))
        print(values) # list of first 10 modes in Hz
    
    • Field Query is for querying field quantities defined at any point in the domain. The available field quantities depend on the physics type.

      • For Stress Simulation, the following FieldType inputs are available

        • Displacement tuple of dimension 3 for each point

          [x-displacement, y-displacement, z-displacement]
          
        • Strain and Stress are each a tuple of dimension 6 for each point

          [stress_xx, stress_yy, stress_zz, stress_yz, stress_xz, stress_xy]
          [strain_xx, strain_yy, strain_zz, strain_yz, strain_xz, strain_xy]
          
        • TopologicalSensitivity, StrainEnergyDensity, and VonMisesStress are each a tuple of dimension 1 for each point

      • For Modal Simulation and Linear Buckling Simulation, the only FieldType available is Displacement, which corresponds to the mode shape information and requires an index for the specified mode.

        # Create a field query for the displacement/mode shape of mode 1
        mode_num = 0 # first mode
        modal_field_query = FieldQuery(f=Field.Displacement, scheme=DiscreteIndex(mode_num))
        
      • For Thermal Simulation, the available FieldTypes are

        • Temperature which is a tuple of dimension 1

        • HeatFlux which is a tuple of dimension 3

      • Field Query Interface usage

        # FieldQuery example
        # Create a FieldQuery to query displacement
        results = QueryResult(assembly)
        displacement_query = FieldQuery(f=Field.Displacement)
        
        # Create a FieldQuery with optional input to query the y-component
        y_displacement_query = FieldQuery(f=Field.Displacement, component=1)
        
        # FieldQuery with optional input to query the norm of displacement
        displacement_magnitude_query = FieldQuery(f=Field.Displacement, norm=True)
        
        # Sample the simulation with the given query to create a VectorArray
        # * more info on VectorArrays are provided in the subsequent section *
        displacement_VectorArray1 = simulator.sample(displacement_query, results)
        displacement_VectorArray2 = simulator.sample(y_displacement_query, results)
        displacement_VectorArray3 = simulator.sample(displacement_magnitude_query, results)
        
        displacement1 = displacement_VectorArray1.get(i, j); # jth component of the displacement (0 = X, 1 = Y, or 2 = Z) at the ith sampling point
        displacement2 = displacement_VectorArray2.get(1, 0); # y-displacement value at the second sampling point
        displacement3 = displacement_VectorArray3.get(1, 0); # displacement magnitude at the second sampling point
        
    • Statistical queries allow querying for the maximum, minimum or mean of any field quantity. The following example queries for the maximum von Mises stress.

      # Create a StatisticalQuery to query for the maximum von Mises stress
      results = QueryResult(assembly)
      max_von_mises_query = StatisticalQuery(f=Field.VonMisesStress, s=StatisticalQueryType.Maximum)
      
      max_von_mises = simulator.sample(max_von_mises_query, results).get(0, 0)
      

Sample and Export Results

  • Sampling results in-memory

    • Methods to sample results of the simulation and store/print VectorArray results from the queries created above.

      # Query results
      results = QueryResult(assembly)
      stress_query = FieldQuery(f=Field.Stress) # a field is needed to create a field query
      stress_VectorArray = simulator.sample(stress_query, results)
      
    • This sampling is stored in a VectorArray of size n_tuple by dimension, n_tuple is the number of points sampled in the component/assembly and dimension depends on the query type. For example, FieldQuery(Field.Displacement) has a dimension of 3 for each displacement component, thus VectorArray.get(i, 2) would get the z-displacement of point i.

      # Get the number of tuples (vectors) in the VectorArray (one per point results are sampled at)
      n_tuples = stress_VectorArray.n_tuples()
      
      # Get the dimension of each vector - in this case 6, one value for each stress component
      dimension = stress_VectorArray.dimension()
      
      stress_xx = stress_VectorArray.get(i, 0); # stress_xx at i-th sample point
      stress_yy = stress_VectorArray.get(i, 1); # stress_yy at i-th sample point
      stress_ij = stress_VectorArray.get(i, j); # jth stress component (0, 1, 2, 3, 4, or 5) of the ith tuple
      
  • Export results to a file: Once the query is created, results can be used to write a .vtu file which contains all solution fields via results.writeVTK which has two inputs:

    • file name "*.vtu"

    • UnitSystem is an attribute in the *.vtu ( Note that this argument is just metadata in the *.vtu file. The result magnitudes are in the unit of the scenario regardless of the unit system specified. )

    # Write results to a file (unit system is saved as a metadata in the vtu file)
    results = QueryResult(assembly)
    stress_query = FieldQuery(Field.VonMisesStress)
    simulator.sample(stress_query, results)
    
    # Write results to a file (unit system is saved as a metadata in the vtu file)
    results.writeVTK("cantilever_beam_results.vtu", UnitSystem.MeterKilogramSecond)
    

Sampling on Custom Geometries

  • Another important functionality is the ability to specify a point set to sample on. This can be done by creating a new sampling_component consisting of vertices at the desired sampling locations. Note that the sampling_component is not to be included in the assembly that is being simulated.

    # Create an empty MeshModel to store vertices for desired sampling locations
    sampling_geometry = MeshModel()
    
    # Add the desired sampling point locations and store their ID for later use
    test_index1 = sampling_geometry.addVertex([6.0, 0.0, 0.0])
    test_index2 = sampling_geometry.addVertex([3.0, 0.0, 0.0])
    
    sampling_component = MaterialDomain(sample_geometry, "material_name", scenario)
    
    # Define the assembly to be simulated (note that the sampling_component is not included)
    component = MaterialDomain(component_geometry, "material_name", scenario)
    assembly = [component]
    simulator = StressSimulator(assembly, scenario)
    simulator.solve()
    
    displacement_query = FieldQuery(f=Field.Displacement)
    sampled_results = QueryResult(sampling_component) # sample only the custom point set
    sampled_VectorArray = simulator.sample(displacement_query, sampled_results)
    
    displacement_index1 = sampled_VectorArray.get(test_index1, 0) # x-displacement at above specified index1
    displacement_index2 = sampled_VectorArray.get(test_index2, 1) # y-displacement at above specified index2
    
  • It also possible to provide only a collection of points to sample on, avoiding the cost of constructing the sampling geometry.

    # After simulation has been performed
    
    # Create the collection of sample points
    points = [[1.0, 1.0, 1.0], [2.0, 2.0, 2.0]]
    
    # Note that the material name, in this example "Steel", has to be a material defined in the simulation scenario.
    point_query = simulator.sample(points, "Steel", [FieldQuery(f=Field.VonMisesStress, FieldQuery(f=Field.Displacement)])
    
    # For each point, the results contain the field value for each field in query order
    for p in point_query:
       if (p.dimension() == 1):
          # Print single valued fields, such as von Mises stress
          print(p.get(0, 0))
       if p.dimension() == 3:
          # Print fields with multiple components, such as displacement
          print(f"({p.get(0, 0)}, {p.get(0, 1)}, {p.get(0, 2)})")