feat: Add AtomizerField training data export and intelligent model discovery

Major additions:
- Training data export system for AtomizerField neural network training
- Bracket stiffness optimization study with 50+ training samples
- Intelligent NX model discovery (auto-detect solutions, expressions, mesh)
- Result extractors module for displacement, stress, frequency, mass
- User-generated NX journals for advanced workflows
- Archive structure for legacy scripts and test outputs
- Protocol documentation and dashboard launcher

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-26 12:01:50 -05:00
parent a0c008a593
commit 2b3573ec42
949 changed files with 1405144 additions and 470 deletions

View File

@@ -1,333 +0,0 @@
# Auto-generated journal for solving Beam_sim1.sim
import sys
sys.argv = ['', r'C:\Users\antoi\Documents\Atomaste\Atomizer\studies\uav_arm_optimization\1_setup\model\Beam_sim1.sim', None, 'beam_half_core_thickness=5.543272595780111', 'beam_face_thickness=2.36655422332811', 'holes_diameter=27.930376572591207', 'hole_count=13.212098121132765'] # Set argv for the main function
"""
NX Journal Script to Solve Simulation in Batch Mode
This script opens a .sim file, updates the FEM, and solves it through the NX API.
Usage: run_journal.exe solve_simulation.py <sim_file_path>
Based on recorded NX journal pattern for solving simulations.
"""
import sys
import NXOpen
import NXOpen.Assemblies
import NXOpen.CAE
def main(args):
"""
Open and solve a simulation file with updated expression values.
Args:
args: Command line arguments
args[0]: .sim file path
args[1]: solution_name (optional, e.g., "Solution_Normal_Modes" or None for default)
args[2+]: expression updates as "name=value" pairs
"""
if len(args) < 1:
print("ERROR: No .sim file path provided")
print("Usage: run_journal.exe solve_simulation.py <sim_file_path> [solution_name] [expr1=val1] [expr2=val2] ...")
return False
sim_file_path = args[0]
# Parse solution name if provided (args[1])
solution_name = args[1] if len(args) > 1 and args[1] != 'None' else None
# Extract base name from sim file (e.g., "Beam_sim1.sim" -> "Beam")
import os
sim_filename = os.path.basename(sim_file_path)
part_base_name = sim_filename.split('_sim')[0] if '_sim' in sim_filename else sim_filename.split('.sim')[0]
# Parse expression updates from args[2+] as "name=value" pairs
expression_updates = {}
for arg in args[2:]:
if '=' in arg:
name, value = arg.split('=', 1)
expression_updates[name] = float(value)
print(f"[JOURNAL] Opening simulation: {sim_file_path}")
print(f"[JOURNAL] Detected part base name: {part_base_name}")
if solution_name:
print(f"[JOURNAL] Will solve specific solution: {solution_name}")
else:
print(f"[JOURNAL] Will solve default solution (Solution 1)")
if expression_updates:
print(f"[JOURNAL] Will update expressions:")
for name, value in expression_updates.items():
print(f"[JOURNAL] {name} = {value}")
try:
theSession = NXOpen.Session.GetSession()
# Set load options to load linked parts from directory
print("[JOURNAL] Setting load options for linked parts...")
import os
working_dir = os.path.dirname(os.path.abspath(sim_file_path))
# Complete load options setup (from recorded journal)
theSession.Parts.LoadOptions.LoadLatest = False
theSession.Parts.LoadOptions.ComponentLoadMethod = NXOpen.LoadOptions.LoadMethod.FromDirectory
searchDirectories = [working_dir]
searchSubDirs = [True]
theSession.Parts.LoadOptions.SetSearchDirectories(searchDirectories, searchSubDirs)
theSession.Parts.LoadOptions.ComponentsToLoad = NXOpen.LoadOptions.LoadComponents.All
theSession.Parts.LoadOptions.PartLoadOption = NXOpen.LoadOptions.LoadOption.FullyLoad
theSession.Parts.LoadOptions.SetInterpartData(True, NXOpen.LoadOptions.Parent.All)
theSession.Parts.LoadOptions.AllowSubstitution = False
theSession.Parts.LoadOptions.GenerateMissingPartFamilyMembers = True
theSession.Parts.LoadOptions.AbortOnFailure = False
referenceSets = ["As Saved", "Use Simplified", "Use Model", "Entire Part", "Empty"]
theSession.Parts.LoadOptions.SetDefaultReferenceSets(referenceSets)
theSession.Parts.LoadOptions.ReferenceSetOverride = False
print(f"[JOURNAL] Load directory set to: {working_dir}")
# Close any currently open sim file to force reload from disk
print("[JOURNAL] Checking for open parts...")
try:
current_work = theSession.Parts.BaseWork
if current_work and hasattr(current_work, 'FullPath'):
current_path = current_work.FullPath
print(f"[JOURNAL] Closing currently open part: {current_path}")
# Close without saving (we want to reload from disk)
partCloseResponses1 = [NXOpen.BasePart.CloseWholeTree]
theSession.Parts.CloseAll(partCloseResponses1)
print("[JOURNAL] Parts closed")
except Exception as e:
print(f"[JOURNAL] No parts to close or error closing: {e}")
# Open the .sim file (now will load fresh from disk with updated .prt files)
print(f"[JOURNAL] Opening simulation fresh from disk...")
basePart1, partLoadStatus1 = theSession.Parts.OpenActiveDisplay(
sim_file_path,
NXOpen.DisplayPartOption.AllowAdditional
)
workSimPart = theSession.Parts.BaseWork
displaySimPart = theSession.Parts.BaseDisplay
print(f"[JOURNAL] Simulation opened successfully")
partLoadStatus1.Dispose()
# Switch to simulation application
theSession.ApplicationSwitchImmediate("UG_APP_SFEM")
simPart1 = workSimPart
theSession.Post.UpdateUserGroupsFromSimPart(simPart1)
# STEP 1: Try to switch to part and update expressions (optional for some models)
print(f"[JOURNAL] STEP 1: Checking for {part_base_name}.prt geometry...")
geometry_updated = False
try:
# Find the main part (may not exist for embedded geometry models)
bracketPart = None
try:
bracketPart = theSession.Parts.FindObject(part_base_name)
except:
pass
if bracketPart:
print(f"[JOURNAL] Found {part_base_name} part, updating geometry...")
# Make Bracket the active display part
status, partLoadStatus = theSession.Parts.SetActiveDisplay(
bracketPart,
NXOpen.DisplayPartOption.AllowAdditional,
NXOpen.PartDisplayPartWorkPartOption.UseLast
)
partLoadStatus.Dispose()
workPart = theSession.Parts.Work
# CRITICAL: Apply expression changes BEFORE updating geometry
expressions_updated = []
# Apply all expression updates dynamically
for expr_name, expr_value in expression_updates.items():
print(f"[JOURNAL] Applying {expr_name} = {expr_value}")
try:
expr_obj = workPart.Expressions.FindObject(expr_name)
if expr_obj:
# Use millimeters as default unit for geometric parameters
unit_mm = workPart.UnitCollection.FindObject("MilliMeter")
workPart.Expressions.EditExpressionWithUnits(expr_obj, unit_mm, str(expr_value))
expressions_updated.append(expr_obj)
print(f"[JOURNAL] {expr_name} updated successfully")
else:
print(f"[JOURNAL] WARNING: {expr_name} expression not found!")
except Exception as e:
print(f"[JOURNAL] ERROR updating {expr_name}: {e}")
# Make expressions up to date
if expressions_updated:
print(f"[JOURNAL] Making {len(expressions_updated)} expression(s) up to date...")
for expr in expressions_updated:
markId_expr = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Invisible, "Make Up to Date")
objects1 = [expr]
theSession.UpdateManager.MakeUpToDate(objects1, markId_expr)
theSession.DeleteUndoMark(markId_expr, None)
# CRITICAL: Update the geometry model - rebuilds features with new expressions
print(f"[JOURNAL] Rebuilding geometry with new expression values...")
markId_update = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Invisible, "NX update")
nErrs = theSession.UpdateManager.DoUpdate(markId_update)
theSession.DeleteUndoMark(markId_update, "NX update")
print(f"[JOURNAL] {part_base_name} geometry updated ({nErrs} errors)")
# Extract mass from expression p173 if it exists and write to temp file
try:
mass_expr = workPart.Expressions.FindObject("p173")
if mass_expr:
mass_kg = mass_expr.Value
mass_output_file = os.path.join(working_dir, "_temp_mass.txt")
with open(mass_output_file, 'w') as f:
f.write(str(mass_kg))
print(f"[JOURNAL] Mass from p173: {mass_kg:.6f} kg ({mass_kg * 1000:.2f} g)")
print(f"[JOURNAL] Mass written to: {mass_output_file}")
except:
pass # Expression p173 might not exist in all models
geometry_updated = True
else:
print(f"[JOURNAL] {part_base_name} part not found - may be embedded in sim file")
except Exception as e:
print(f"[JOURNAL] Could not update {part_base_name}.prt: {e}")
print(f"[JOURNAL] Continuing with sim-only solve...")
# STEP 2: Try to switch to FEM part and update (optional for some models)
fem_part_name = f"{part_base_name}_fem1"
print(f"[JOURNAL] STEP 2: Checking for {fem_part_name}.fem...")
fem_updated = False
try:
# Find the FEM part (may not exist or may have different name)
femPart1 = None
try:
femPart1 = theSession.Parts.FindObject(fem_part_name)
except:
# Try with _i suffix for idealized FEM
try:
femPart1 = theSession.Parts.FindObject(f"{fem_part_name}_i")
except:
pass
if femPart1:
print(f"[JOURNAL] Found FEM part, updating...")
# Make FEM the active display part
status, partLoadStatus = theSession.Parts.SetActiveDisplay(
femPart1,
NXOpen.DisplayPartOption.AllowAdditional,
NXOpen.PartDisplayPartWorkPartOption.SameAsDisplay
)
partLoadStatus.Dispose()
workFemPart = theSession.Parts.BaseWork
# CRITICAL: Update FE Model - regenerates FEM with new geometry
print("[JOURNAL] Updating FE Model...")
fEModel1 = workFemPart.FindObject("FEModel")
if fEModel1:
fEModel1.UpdateFemodel()
print("[JOURNAL] FE Model updated with new geometry!")
fem_updated = True
else:
print("[JOURNAL] WARNING: Could not find FEModel object")
else:
print(f"[JOURNAL] FEM part not found - may be embedded in sim file")
except Exception as e:
print(f"[JOURNAL] Could not update FEM: {e}")
print(f"[JOURNAL] Continuing with sim-only solve...")
# STEP 3: Switch back to sim part
print("[JOURNAL] STEP 3: Switching back to sim part...")
try:
status, partLoadStatus = theSession.Parts.SetActiveDisplay(
simPart1,
NXOpen.DisplayPartOption.AllowAdditional,
NXOpen.PartDisplayPartWorkPartOption.UseLast
)
partLoadStatus.Dispose()
workSimPart = theSession.Parts.BaseWork
print("[JOURNAL] Switched back to sim part")
except Exception as e:
print(f"[JOURNAL] WARNING: Error switching to sim part: {e}")
# Note: Old output files are deleted by nx_solver.py before calling this journal
# This ensures NX performs a fresh solve
# Solve the simulation
print("[JOURNAL] Starting solve...")
markId3 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Visible, "Start")
theSession.SetUndoMarkName(markId3, "Solve Dialog")
markId5 = theSession.SetUndoMark(NXOpen.Session.MarkVisibility.Invisible, "Solve")
theCAESimSolveManager = NXOpen.CAE.SimSolveManager.GetSimSolveManager(theSession)
# Get the simulation object
simSimulation1 = workSimPart.FindObject("Simulation")
# Get the solution(s) to solve - either specific or all
if solution_name:
# Solve specific solution in background mode
solution_obj_name = f"Solution[{solution_name}]"
print(f"[JOURNAL] Looking for solution: {solution_obj_name}")
simSolution1 = simSimulation1.FindObject(solution_obj_name)
psolutions1 = [simSolution1]
numsolutionssolved1, numsolutionsfailed1, numsolutionsskipped1 = theCAESimSolveManager.SolveChainOfSolutions(
psolutions1,
NXOpen.CAE.SimSolution.SolveOption.Solve,
NXOpen.CAE.SimSolution.SetupCheckOption.CompleteDeepCheckAndOutputErrors,
NXOpen.CAE.SimSolution.SolveMode.Background
)
else:
# Solve ALL solutions using SolveAllSolutions API (Foreground mode)
# This ensures all solutions (static + modal, etc.) complete before returning
print(f"[JOURNAL] Solving all solutions using SolveAllSolutions API (Foreground mode)...")
numsolutionssolved1, numsolutionsfailed1, numsolutionsskipped1 = theCAESimSolveManager.SolveAllSolutions(
NXOpen.CAE.SimSolution.SolveOption.Solve,
NXOpen.CAE.SimSolution.SetupCheckOption.CompleteCheckAndOutputErrors,
NXOpen.CAE.SimSolution.SolveMode.Foreground,
False
)
theSession.DeleteUndoMark(markId5, None)
theSession.SetUndoMarkName(markId3, "Solve")
print(f"[JOURNAL] Solve completed!")
print(f"[JOURNAL] Solutions solved: {numsolutionssolved1}")
print(f"[JOURNAL] Solutions failed: {numsolutionsfailed1}")
print(f"[JOURNAL] Solutions skipped: {numsolutionsskipped1}")
# NOTE: When solution_name=None, we use Foreground mode to ensure all solutions
# complete before returning. When solution_name is specified, Background mode is used.
# Save the simulation to write all output files
print("[JOURNAL] Saving simulation to ensure output files are written...")
simPart2 = workSimPart
partSaveStatus1 = simPart2.Save(
NXOpen.BasePart.SaveComponents.TrueValue,
NXOpen.BasePart.CloseAfterSave.FalseValue
)
partSaveStatus1.Dispose()
print("[JOURNAL] Save complete!")
return True
except Exception as e:
print(f"[JOURNAL] ERROR: {e}")
import traceback
traceback.print_exc()
return False
if __name__ == '__main__':
success = main(sys.argv[1:])
sys.exit(0 if success else 1)

View File

@@ -0,0 +1,605 @@
{
"n_samples": 100,
"samples": [
{
"beam_half_core_thickness": 5,
"beam_face_thickness": 1,
"holes_diameter": 10,
"hole_count": 8
},
{
"beam_half_core_thickness": 10,
"beam_face_thickness": 1,
"holes_diameter": 10,
"hole_count": 8
},
{
"beam_half_core_thickness": 5,
"beam_face_thickness": 3,
"holes_diameter": 10,
"hole_count": 8
},
{
"beam_half_core_thickness": 10,
"beam_face_thickness": 3,
"holes_diameter": 10,
"hole_count": 8
},
{
"beam_half_core_thickness": 5,
"beam_face_thickness": 1,
"holes_diameter": 50,
"hole_count": 8
},
{
"beam_half_core_thickness": 10,
"beam_face_thickness": 1,
"holes_diameter": 50,
"hole_count": 8
},
{
"beam_half_core_thickness": 5,
"beam_face_thickness": 3,
"holes_diameter": 50,
"hole_count": 8
},
{
"beam_half_core_thickness": 10,
"beam_face_thickness": 3,
"holes_diameter": 50,
"hole_count": 8
},
{
"beam_half_core_thickness": 5,
"beam_face_thickness": 1,
"holes_diameter": 10,
"hole_count": 14
},
{
"beam_half_core_thickness": 10,
"beam_face_thickness": 1,
"holes_diameter": 10,
"hole_count": 14
},
{
"beam_half_core_thickness": 5,
"beam_face_thickness": 3,
"holes_diameter": 10,
"hole_count": 14
},
{
"beam_half_core_thickness": 10,
"beam_face_thickness": 3,
"holes_diameter": 10,
"hole_count": 14
},
{
"beam_half_core_thickness": 5,
"beam_face_thickness": 1,
"holes_diameter": 50,
"hole_count": 14
},
{
"beam_half_core_thickness": 10,
"beam_face_thickness": 1,
"holes_diameter": 50,
"hole_count": 14
},
{
"beam_half_core_thickness": 5,
"beam_face_thickness": 3,
"holes_diameter": 50,
"hole_count": 14
},
{
"beam_half_core_thickness": 10,
"beam_face_thickness": 3,
"holes_diameter": 50,
"hole_count": 14
},
{
"beam_half_core_thickness": 7,
"beam_face_thickness": 2,
"holes_diameter": 39.11495337147077,
"hole_count": 12
},
{
"beam_half_core_thickness": 5,
"beam_face_thickness": 3,
"holes_diameter": 49.16136204667126,
"hole_count": 9
},
{
"beam_half_core_thickness": 10,
"beam_face_thickness": 2,
"holes_diameter": 46.01390570274639,
"hole_count": 9
},
{
"beam_half_core_thickness": 6,
"beam_face_thickness": 1,
"holes_diameter": 21.217421810082225,
"hole_count": 14
},
{
"beam_half_core_thickness": 6,
"beam_face_thickness": 2,
"holes_diameter": 20.082080394289246,
"hole_count": 8
},
{
"beam_half_core_thickness": 7,
"beam_face_thickness": 2,
"holes_diameter": 18.109191416954808,
"hole_count": 10
},
{
"beam_half_core_thickness": 10,
"beam_face_thickness": 3,
"holes_diameter": 14.539656664891888,
"hole_count": 10
},
{
"beam_half_core_thickness": 10,
"beam_face_thickness": 2,
"holes_diameter": 30.59773230671056,
"hole_count": 13
},
{
"beam_half_core_thickness": 6,
"beam_face_thickness": 3,
"holes_diameter": 21.681163899392473,
"hole_count": 14
},
{
"beam_half_core_thickness": 7,
"beam_face_thickness": 1,
"holes_diameter": 28.46337650045196,
"hole_count": 12
},
{
"beam_half_core_thickness": 6,
"beam_face_thickness": 1,
"holes_diameter": 15.380826141903691,
"hole_count": 12
},
{
"beam_half_core_thickness": 10,
"beam_face_thickness": 1,
"holes_diameter": 38.386915057604675,
"hole_count": 9
},
{
"beam_half_core_thickness": 10,
"beam_face_thickness": 2,
"holes_diameter": 40.857186570249944,
"hole_count": 9
},
{
"beam_half_core_thickness": 9,
"beam_face_thickness": 1,
"holes_diameter": 44.42611172446365,
"hole_count": 11
},
{
"beam_half_core_thickness": 8,
"beam_face_thickness": 2,
"holes_diameter": 48.98104904851064,
"hole_count": 10
},
{
"beam_half_core_thickness": 9,
"beam_face_thickness": 3,
"holes_diameter": 35.445125663580384,
"hole_count": 14
},
{
"beam_half_core_thickness": 5,
"beam_face_thickness": 3,
"holes_diameter": 34.971806113930754,
"hole_count": 8
},
{
"beam_half_core_thickness": 7,
"beam_face_thickness": 2,
"holes_diameter": 23.231150155800222,
"hole_count": 13
},
{
"beam_half_core_thickness": 9,
"beam_face_thickness": 2,
"holes_diameter": 44.25795107538616,
"hole_count": 13
},
{
"beam_half_core_thickness": 6,
"beam_face_thickness": 3,
"holes_diameter": 24.49665135602796,
"hole_count": 13
},
{
"beam_half_core_thickness": 6,
"beam_face_thickness": 2,
"holes_diameter": 28.659990293016172,
"hole_count": 11
},
{
"beam_half_core_thickness": 9,
"beam_face_thickness": 2,
"holes_diameter": 38.70363840447788,
"hole_count": 13
},
{
"beam_half_core_thickness": 7,
"beam_face_thickness": 1,
"holes_diameter": 18.02270852251754,
"hole_count": 11
},
{
"beam_half_core_thickness": 5,
"beam_face_thickness": 3,
"holes_diameter": 22.713565671834345,
"hole_count": 13
},
{
"beam_half_core_thickness": 8,
"beam_face_thickness": 2,
"holes_diameter": 32.8009495704188,
"hole_count": 13
},
{
"beam_half_core_thickness": 7,
"beam_face_thickness": 2,
"holes_diameter": 37.968633351685945,
"hole_count": 10
},
{
"beam_half_core_thickness": 9,
"beam_face_thickness": 3,
"holes_diameter": 27.88125642755058,
"hole_count": 11
},
{
"beam_half_core_thickness": 10,
"beam_face_thickness": 1,
"holes_diameter": 24.06867795722209,
"hole_count": 14
},
{
"beam_half_core_thickness": 8,
"beam_face_thickness": 2,
"holes_diameter": 10.868203436695604,
"hole_count": 11
},
{
"beam_half_core_thickness": 8,
"beam_face_thickness": 3,
"holes_diameter": 46.46090807629757,
"hole_count": 8
},
{
"beam_half_core_thickness": 8,
"beam_face_thickness": 1,
"holes_diameter": 45.197883656600055,
"hole_count": 10
},
{
"beam_half_core_thickness": 6,
"beam_face_thickness": 3,
"holes_diameter": 18.89054227984153,
"hole_count": 13
},
{
"beam_half_core_thickness": 9,
"beam_face_thickness": 1,
"holes_diameter": 13.728289062601483,
"hole_count": 10
},
{
"beam_half_core_thickness": 5,
"beam_face_thickness": 2,
"holes_diameter": 16.3852783373898,
"hole_count": 9
},
{
"beam_half_core_thickness": 9,
"beam_face_thickness": 1,
"holes_diameter": 26.293935606071294,
"hole_count": 12
},
{
"beam_half_core_thickness": 7,
"beam_face_thickness": 3,
"holes_diameter": 47.17246359286906,
"hole_count": 11
},
{
"beam_half_core_thickness": 7,
"beam_face_thickness": 2,
"holes_diameter": 40.31830047745593,
"hole_count": 13
},
{
"beam_half_core_thickness": 7,
"beam_face_thickness": 2,
"holes_diameter": 39.60652765514554,
"hole_count": 10
},
{
"beam_half_core_thickness": 10,
"beam_face_thickness": 1,
"holes_diameter": 30.42448774231645,
"hole_count": 9
},
{
"beam_half_core_thickness": 8,
"beam_face_thickness": 2,
"holes_diameter": 10.130002564625293,
"hole_count": 8
},
{
"beam_half_core_thickness": 8,
"beam_face_thickness": 3,
"holes_diameter": 29.890374290050058,
"hole_count": 9
},
{
"beam_half_core_thickness": 9,
"beam_face_thickness": 1,
"holes_diameter": 34.36676678898456,
"hole_count": 10
},
{
"beam_half_core_thickness": 9,
"beam_face_thickness": 1,
"holes_diameter": 13.194801029457086,
"hole_count": 12
},
{
"beam_half_core_thickness": 7,
"beam_face_thickness": 3,
"holes_diameter": 23.731139134484753,
"hole_count": 10
},
{
"beam_half_core_thickness": 6,
"beam_face_thickness": 2,
"holes_diameter": 13.861106063688137,
"hole_count": 9
},
{
"beam_half_core_thickness": 5,
"beam_face_thickness": 2,
"holes_diameter": 11.657686495054072,
"hole_count": 11
},
{
"beam_half_core_thickness": 8,
"beam_face_thickness": 1,
"holes_diameter": 46.96492489101252,
"hole_count": 12
},
{
"beam_half_core_thickness": 9,
"beam_face_thickness": 2,
"holes_diameter": 11.9348508806982,
"hole_count": 9
},
{
"beam_half_core_thickness": 5,
"beam_face_thickness": 2,
"holes_diameter": 36.11748365875024,
"hole_count": 12
},
{
"beam_half_core_thickness": 6,
"beam_face_thickness": 2,
"holes_diameter": 36.573126349659724,
"hole_count": 11
},
{
"beam_half_core_thickness": 7,
"beam_face_thickness": 1,
"holes_diameter": 20.713932768475075,
"hole_count": 12
},
{
"beam_half_core_thickness": 8,
"beam_face_thickness": 3,
"holes_diameter": 41.36579894391118,
"hole_count": 14
},
{
"beam_half_core_thickness": 8,
"beam_face_thickness": 2,
"holes_diameter": 33.00273217350339,
"hole_count": 13
},
{
"beam_half_core_thickness": 6,
"beam_face_thickness": 2,
"holes_diameter": 17.27837755732581,
"hole_count": 12
},
{
"beam_half_core_thickness": 7,
"beam_face_thickness": 2,
"holes_diameter": 45.27258926142547,
"hole_count": 9
},
{
"beam_half_core_thickness": 10,
"beam_face_thickness": 2,
"holes_diameter": 49.684350374874484,
"hole_count": 8
},
{
"beam_half_core_thickness": 8,
"beam_face_thickness": 2,
"holes_diameter": 34.27387643267321,
"hole_count": 9
},
{
"beam_half_core_thickness": 6,
"beam_face_thickness": 2,
"holes_diameter": 37.552219239493375,
"hole_count": 14
},
{
"beam_half_core_thickness": 6,
"beam_face_thickness": 2,
"holes_diameter": 10.984274656380851,
"hole_count": 9
},
{
"beam_half_core_thickness": 9,
"beam_face_thickness": 3,
"holes_diameter": 19.99150629334429,
"hole_count": 8
},
{
"beam_half_core_thickness": 7,
"beam_face_thickness": 2,
"holes_diameter": 15.127313932396937,
"hole_count": 13
},
{
"beam_half_core_thickness": 8,
"beam_face_thickness": 2,
"holes_diameter": 41.61756802031489,
"hole_count": 11
},
{
"beam_half_core_thickness": 8,
"beam_face_thickness": 3,
"holes_diameter": 42.07050903543811,
"hole_count": 12
},
{
"beam_half_core_thickness": 10,
"beam_face_thickness": 1,
"holes_diameter": 31.315875065239247,
"hole_count": 10
},
{
"beam_half_core_thickness": 8,
"beam_face_thickness": 2,
"holes_diameter": 27.111162449068964,
"hole_count": 12
},
{
"beam_half_core_thickness": 7,
"beam_face_thickness": 3,
"holes_diameter": 32.23317396479806,
"hole_count": 12
},
{
"beam_half_core_thickness": 6,
"beam_face_thickness": 1,
"holes_diameter": 42.72156186555336,
"hole_count": 10
},
{
"beam_half_core_thickness": 5,
"beam_face_thickness": 2,
"holes_diameter": 22.354573614683687,
"hole_count": 12
},
{
"beam_half_core_thickness": 9,
"beam_face_thickness": 3,
"holes_diameter": 25.86632641580976,
"hole_count": 11
},
{
"beam_half_core_thickness": 9,
"beam_face_thickness": 1,
"holes_diameter": 16.726188122745427,
"hole_count": 11
},
{
"beam_half_core_thickness": 6,
"beam_face_thickness": 2,
"holes_diameter": 16.077740537569333,
"hole_count": 10
},
{
"beam_half_core_thickness": 9,
"beam_face_thickness": 2,
"holes_diameter": 37.11940280003448,
"hole_count": 9
},
{
"beam_half_core_thickness": 7,
"beam_face_thickness": 2,
"holes_diameter": 42.936448307719736,
"hole_count": 14
},
{
"beam_half_core_thickness": 8,
"beam_face_thickness": 1,
"holes_diameter": 12.455440499239165,
"hole_count": 10
},
{
"beam_half_core_thickness": 7,
"beam_face_thickness": 2,
"holes_diameter": 33.43834402820413,
"hole_count": 13
},
{
"beam_half_core_thickness": 8,
"beam_face_thickness": 1,
"holes_diameter": 31.649491411453504,
"hole_count": 13
},
{
"beam_half_core_thickness": 9,
"beam_face_thickness": 3,
"holes_diameter": 25.05471928757126,
"hole_count": 9
},
{
"beam_half_core_thickness": 5,
"beam_face_thickness": 3,
"holes_diameter": 48.3438679647506,
"hole_count": 11
},
{
"beam_half_core_thickness": 7,
"beam_face_thickness": 3,
"holes_diameter": 47.92076550008274,
"hole_count": 8
},
{
"beam_half_core_thickness": 5,
"beam_face_thickness": 1,
"holes_diameter": 25.572915964489866,
"hole_count": 13
},
{
"beam_half_core_thickness": 6,
"beam_face_thickness": 2,
"holes_diameter": 43.65316133095015,
"hole_count": 11
},
{
"beam_half_core_thickness": 6,
"beam_face_thickness": 2,
"holes_diameter": 29.14076579621568,
"hole_count": 10
},
{
"beam_half_core_thickness": 9,
"beam_face_thickness": 2,
"holes_diameter": 19.173860398899862,
"hole_count": 11
},
{
"beam_half_core_thickness": 8,
"beam_face_thickness": 2,
"holes_diameter": 27.556141506046885,
"hole_count": 12
}
]
}