Hi
I’m running into issues when joining “Karamba scripting” and “Karamba components”.
Error balloon: “1. Data conversion failed from Model to Model”
The thing is that the whole script is running fine on other systems but on my laptop I’m getting errors.
I’ve tried purging my plug-in’s and reinstalling, Reinstalling Rhino.
Hope someone can help.
Thanks!
I’m running rhino 8 and Karamba 3.1.50414.
The Karamba script:
#region Usings
using System;
#r "C:\Users\name\AppData\Roaming\McNeel\Rhinoceros\packages\8.0\Karamba3D\3.1.50414\net8.0-windows\Karamba.gha"
#r "C:\Users\name\AppData\Roaming\McNeel\Rhinoceros\packages\8.0\Karamba3D\3.1.50414\net8.0-windows\KarambaCommon.dll"
using System;
using System.Linq;
using System.Collections.Generic;
using Rhino;
using Rhino.Geometry;
using Grasshopper;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Types;
using Karamba.Models;
using Karamba.CrossSections;
using Karamba.Elements;
using Karamba.Results;
using Karamba.Geometry;
#endregion
public class Script_Instance : GH_ScriptInstance
{
/*
─────────────────────────────────────────────────────────────────────────────────
NOTES:
- This script performs an iterative cross-section selection for timber beams
following an EC5-style check (axial + bending).
- Custom material strength values (charFm, charFc, charFt) are provided as input.
- The model is analyzed repeatedly until either the cross-sections do not change
or the user-specified iteration limit (niter) is reached.
- When niter = 0, the script performs a single analysis with the initial cross-sections.
- Force redistribution is accounted for by re-analyzing the model after each
batch of cross-section changes.
- OPTIMIZATION: Multiple performance improvements to reduce calculation time.
─────────────────────────────────────────────────────────────────────────────────
*/
// Partial factor (EC5, typically ~1.3)..
private const double GAMMA_M = 1.3;
// Material property lists (populated from input)
private List<double> charFmValues = new List<double>();
private List<double> charFcValues = new List<double>();
private List<double> charFtValues = new List<double>();
// OPTIMIZATION: Pre-calculate design strengths for all sections
private List<double> designFmValues = new List<double>();
private List<double> designFcValues = new List<double>();
private List<double> designFtValues = new List<double>();
// OPTIMIZATION: Cache cross-section properties for faster access
private List<double> sectionAreas = new List<double>();
private List<double> sectionWelY = new List<double>();
// =================== HELPER METHODS ===================
// OPTIMIZATION: Calculate all element forces at once to reduce calls
private Dictionary<int, Tuple<double, double>> CalculateAllElementForces(Model workModel, string lcName, List<int> elementIndices)
{
var result = new Dictionary<int, Tuple<double, double>>();
// Convert element indices to strings for Karamba API
var elemIndicesStr = elementIndices.Select(idx => idx.ToString()).ToList();
// Get forces for all elements at once
List<List<double>> N, V, M;
BeamResultantForces.solve(workModel, elemIndicesStr, lcName, 10, 5, out N, out V, out M);
// Store results in dictionary
// The first list is for the load case (always index 0 as we're only using one load case)
// The second list contains values for each element
for (int i = 0; i < elementIndices.Count; i++)
{
double axial = N[0][i]; // kN (keeping sign)
double moment = Math.Abs(M[0][i]); // kN*m (absolute value)
result[elementIndices[i]] = new Tuple<double, double>(axial, moment);
}
return result;
}
// OPTIMIZATION: Streamlined interaction ratio calculation with pre-calculated values
private double CalculateInteractionRatio(double axial, double moment, int sectionIndex)
{
// Cross-section properties (already pre-calculated)
double A = sectionAreas[sectionIndex];
double Wel_y = sectionWelY[sectionIndex];
// Pre-calculated design strengths
double designFm = designFmValues[sectionIndex];
double designFc = designFcValues[sectionIndex];
double designFt = designFtValues[sectionIndex];
// Calculate utilization based on axial force sign
double utilAxial;
if (axial >= 0) // Tension
{
utilAxial = ((axial * 1000) / A) / designFt; // Use tension strength
}
else // Compression
{
utilAxial = ((Math.Abs(axial) * 1000) / A) / designFc; // Use compression strength
}
double utilBending = ((moment * 1000000) / Wel_y) / designFm; // Use bending strength
return utilAxial + utilBending;
}
// Find appropriate cross-section from the list
private int FindAppropriateSection(double axial, double moment, List<CroSec_Beam> croSecsAll)
{
// OPTIMIZATION: Search through all cross-sections from smallest to largest
for (int i = 0; i < croSecsAll.Count; i++)
{
double interaction = CalculateInteractionRatio(axial, moment, i);
// If interaction ≤ 1.0, this section is adequate
if (interaction <= 1.0)
return i;
}
// If no adequate section found, return the largest one
return croSecsAll.Count - 1;
}
// Main execution script
private void RunScript(
object Model_in,
List<object> CroSecs_in,
DataTree<double> materialProps_in,
int niter,
string lcName,
double kmod,
ref object Model_out,
ref object Disp_out,
ref object force_out,
ref object utilization_out)
{
// 1) VALIDATE INPUTS
var modelInitial = Model_in as Model;
if (modelInitial == null)
throw new ArgumentException("Input 'Model_in' is not a Karamba Model!");
if (string.IsNullOrEmpty(lcName))
throw new ArgumentException("Load case name cannot be empty!");
var croSecsAll = new List<CroSec_Beam>();
foreach(var obj in CroSecs_in)
{
if (obj is CroSec_Beam cs)
croSecsAll.Add(cs);
else
throw new ArgumentException("One or more inputs in 'CroSecs_in' are not CroSec_Beam objects!");
}
if (croSecsAll.Count == 0)
throw new ArgumentException("No cross sections provided!");
// Process material properties data tree
if (materialProps_in == null || materialProps_in.BranchCount < 3)
throw new ArgumentException("Material properties input must contain three branches: charFm, charFc, and charFt!");
// Extract values from the data tree
charFmValues = materialProps_in.Branch(0); // First branch = charFm
charFcValues = materialProps_in.Branch(1); // Second branch = charFc
charFtValues = materialProps_in.Branch(2); // Third branch = charFt
// Verify that we have the correct number of material property values
if (charFmValues.Count != croSecsAll.Count || charFcValues.Count != croSecsAll.Count || charFtValues.Count != croSecsAll.Count)
throw new ArgumentException("Number of material property values must match number of cross sections!");
// 2) PREPARE ANALYSIS
var k3d = new KarambaCommon.Toolkit();
// Clone the model so we don't modify the original input
var workModel = modelInitial.Clone();
workModel.cloneElements();
// Store final utilization values
var finalUtilization = new List<double>();
// Initialize with zeros for all elements
for (int i = 0; i < workModel.elems.Count; i++)
{
finalUtilization.Add(0.0);
}
// Create a map of cross-section names to indices in the croSecsAll list
Dictionary<string, int> crossSectionMap = new Dictionary<string, int>();
for (int i = 0; i < croSecsAll.Count; i++)
{
crossSectionMap[croSecsAll[i].name] = i;
}
// OPTIMIZATION: Create a list of beam elements for faster processing
var beamElements = new List<int>();
for (int e = 0; e < workModel.elems.Count; e++)
{
if (workModel.elems[e] is ModelBeam)
beamElements.Add(e);
}
DateTime startTime = DateTime.Now;
int iteration = 0;
if (niter == 0)
{
// Handle the case where niter = 0
// We perform a single analysis of the initial model without optimization
Print("Running single analysis with initial cross-sections (niter=0)");
// OPTIMIZATION: Pre-calculate design strengths and section properties
PreCalculateProperties(croSecsAll, kmod);
// Analyze the model
IReadOnlyList<double> maxDisp;
IReadOnlyList<Vector3> outForce;
IReadOnlyList<double> outEnergy;
string warning;
workModel = k3d.Algorithms.Analyze(
workModel, new List<string> { lcName },
out maxDisp, out outForce, out outEnergy, out warning
);
if (!string.IsNullOrEmpty(warning))
Print("Analysis warning: " + warning);
// Calculate all element forces
var allForces = CalculateAllElementForces(workModel, lcName, beamElements);
// Calculate utilization for each beam element based on its current cross-section
foreach (int e in beamElements)
{
var beam = workModel.elems[e] as ModelBeam;
if (beam == null) continue;
// Get current cross-section
var currentCroSec = beam.crosec as CroSec_Beam;
if (currentCroSec == null) continue;
// Find the index of this cross-section in our list
if (!crossSectionMap.TryGetValue(currentCroSec.name, out int sectionIndex))
{
Print($"Warning: Cross-section '{currentCroSec.name}' not found in input list. Using first cross-section for calculations.");
sectionIndex = 0;
}
// Get element forces
var forces = allForces[e];
double axial = forces.Item1;
double moment = forces.Item2;
// Calculate interaction ratio
double interaction = CalculateInteractionRatio(axial, moment, sectionIndex);
// Store utilization
finalUtilization[e] = interaction;
}
// Final displacement
Disp_out = maxDisp.Count > 0 ? maxDisp[0] : 0.0;
}
else
{
// Original optimization code for niter > 0
// OPTIMIZATION: Pre-calculate design strengths and section properties
PreCalculateProperties(croSecsAll, kmod);
// Dictionary to keep track of the current section index for each element
var currentSectionIndices = new Dictionary<int, int>();
// 3) ITERATION LOOP
bool anySectionChanged = false;
for (iteration = 0; iteration < niter; iteration++)
{
// Analyze the model for the given load case
IReadOnlyList<double> maxDisp;
IReadOnlyList<Vector3> outForce;
IReadOnlyList<double> outEnergy;
string warning;
workModel = k3d.Algorithms.Analyze(
workModel, new List<string> { lcName }, out maxDisp, out outForce, out outEnergy, out warning
);
if (!string.IsNullOrEmpty(warning))
Print("Analysis warning: " + warning);
// Reset change flag for this iteration
anySectionChanged = false;
// List to collect all changes to apply at once
var changedElements = new List<Tuple<ModelBeam, CroSec_Beam>>();
// OPTIMIZATION: Calculate all element forces once per iteration
var allForces = CalculateAllElementForces(workModel, lcName, beamElements);
// 3a) Evaluate each beam
foreach (int e in beamElements)
{
var beam = workModel.elems[e] as ModelBeam;
if (beam == null) continue;
// Get current cross-section
var oldCroSec = beam.crosec as CroSec_Beam;
if (oldCroSec == null) continue;
// Get element forces from pre-calculated dictionary
var forces = allForces[e];
double axial = forces.Item1;
double moment = forces.Item2;
// Find appropriate cross-section
int newIdx = FindAppropriateSection(axial, moment, croSecsAll);
// Store the current section index
int oldIdx = -1;
if (currentSectionIndices.ContainsKey(e))
oldIdx = currentSectionIndices[e];
currentSectionIndices[e] = newIdx;
// Calculate and store the interaction ratio with the proposed cross-section
double interaction = CalculateInteractionRatio(axial, moment, newIdx);
// Always store the current utilization value
finalUtilization[e] = interaction;
// Check if we need to change the cross-section
if (croSecsAll[newIdx] != oldCroSec)
{
changedElements.Add(new Tuple<ModelBeam, CroSec_Beam>(beam, croSecsAll[newIdx]));
anySectionChanged = true;
}
}
// Apply all changes at once and rebuild model if needed
if (anySectionChanged)
{
foreach (var change in changedElements)
{
change.Item1.crosec = change.Item2;
}
// Rebuild model once per iteration
workModel.initMaterialCroSecLists();
workModel.buildFEModel();
// Log number of changes in this iteration
Print($"Iteration {iteration+1}: Changed {changedElements.Count} element(s)");
}
else
{
// No changes in this iteration => converged early
Print($"Converged after {iteration+1} iterations");
break;
}
} // end iteration loop
// 4) FINAL ANALYSIS FOR OUTPUT
// We need a final analysis to get updated forces after all changes
IReadOnlyList<double> finalMaxDisp;
IReadOnlyList<Vector3> outF;
IReadOnlyList<double> outE;
string finalWarning;
workModel = k3d.Algorithms.Analyze(
workModel, new List<string> { lcName },
out finalMaxDisp, out outF, out outE, out finalWarning
);
// Final displacement
Disp_out = finalMaxDisp.Count > 0 ? finalMaxDisp[0] : 0.0;
}
// Log performance information
TimeSpan processingTime = DateTime.Now - startTime;
Print($"Total processing time: {processingTime.TotalSeconds:F2} seconds");
// 6) OUTPUTS
// Model
Model_out = new Karamba.GHopper.Models.GH_Model(workModel);
// Summary text for force output
if (niter == 0)
{
force_out = $"Single analysis completed (niter=0) in {processingTime.TotalSeconds:F2} seconds.";
}
else
{
force_out = $"Optimization completed after {Math.Min(niter, iteration + 1)} of {niter} iterations in {processingTime.TotalSeconds:F2} seconds.";
}
// Utilization - already populated during analysis
utilization_out = finalUtilization;
}
// OPTIMIZATION: Pre-calculate all properties for faster access during iterations
private void PreCalculateProperties(List<CroSec_Beam> croSecsAll, double kmod)
{
designFmValues.Clear();
designFcValues.Clear();
designFtValues.Clear();
sectionAreas.Clear();
sectionWelY.Clear();
for (int i = 0; i < croSecsAll.Count; i++)
{
// Design strengths
double designFm = kmod * (Math.Abs(charFmValues[i]) * 10) / GAMMA_M;
double designFc = kmod * (Math.Abs(charFcValues[i]) * 10) / GAMMA_M;
double designFt = kmod * (Math.Abs(charFtValues[i]) * 10) / GAMMA_M;
designFmValues.Add(designFm);
designFcValues.Add(designFc);
designFtValues.Add(designFt);
// Cross-section properties
double area = (1000000 * croSecsAll[i].A); // in m²
double welY = (1000000000 * croSecsAll[i].Welz_y_pos); // in m³
sectionAreas.Add(area);
sectionWelY.Add(welY);
}
}
}