# Welcome to Karamba3D Scripting Guide

The official scripting guide using Karamba3D 2.2.0

This manual describes how to use Karamba3D inside C#-scripts. Since C# is a member of the .NET family everything said also applies to code in IronPython, F#, Visual Basic or any other scripting language being based on the Common Language Infrastructure (CLI).

This manual is not an introduction to C#. For introductional material see e.g. [\[1\]](/bibliography) or [\[3\]](/bibliography). More advanced topics – useful yet not necessary for reading this manual – can be found in the fantastic book “C# in Depth” see [\[4\]](/bibliography).

The previous version of this guide contained sections on how to integrate Karamba3D (K3D) in Java, C++ and native Python. Judging from user feedback these options are not widely used. This is why those chapters where left out in this manual. Instead this manual dwells more on the C#-interface of Karamba3D.

{% hint style="info" %}
Examples detailed in this guide can be found in the "[Karamba3D Scripting Examples Github](https://github.com/karamba3d/K3D_Scripting)"-collection.

The help files (**“Karamba3D\_2\_2\_0\_SDKDoc.chm”**) can be found inside this archive, which is also accessible online: [Karamba3D 2.2.0 SDK Documentation](https://www.karamba3d.com/help/2-2-0).

&#x20;[Karamba3D 1.3.3 SDK Documentation](https://www.karamba3d.com/help/2-2-0).
{% endhint %}

Navigate through the following chapters below or in the menu on the left.

## Citing Karamba3D

In case you use Karamba3D for your scientific work, please cite the following paper:

> Preisinger, C. (2013), *Linking Structure and Parametric Geometry*. Architectural Design, 83: 110-113\
> DOI: 10.1002/ad.1564.

## Disclaimer

Although being tested thoroughly Karamba3D probably contains errors – therefore no guarantee can be given that Karamba3D computes correct results. Use of Karamba3D is entirely at your own risk. Please read the [license agreement](https://www.karamba3d.com/buy/license-agreement/) that comes with Karamba3D in case of further questions.

This manual is written by Clemens Preisinger.\
Editing by Georg Lobe.


# 1.1: Scripting with Karamba3D

Sometimes when creating a Grasshopper (GH) definition you may reach a point where it makes sense to switch from GH’s visual computing environment to textual scripting. This is e.g. the case if you want to apply loops, functions or more refined object oriented programming concepts. Other points in favor of a textual approach would be debugging or code reuse.

When installed for Grasshopper Karamba3D consists of three main parts (these files reside in the “Plug-ins”-folder of Rhino - typically ***C:\ProgramFiles\Rhino6\Plug-ins\Karamba\\***.):

* **“karamba.dll”** is a C++ library which does the numeric calculations.&#x20;
* **“karambaCommon.dll”** provides the .NET user interface to the Karamba3D functionality. It features its own set of geometric types (e.g. vectors, points, meshes,. . . ) and is therefore independent from Grasshopper or Rhino.&#x20;
* **“karamba.gha”** connects GH to **“karambaCommon.dll”** and takes care of the graphical user- interface.&#x20;

This scripting guide comes with a help-file (**“Karamba3D\_1\_3\_3\_SDKDoc.chm”** also accessible online [Karamba3D 1.3.3 SDK Documentation](https://karamba3d.com/help/1-3-3)) which covers **“karambaCommon.dll”** and parts of **“karamba.gha”**. It can be found in the “[Karamba3D Scripting Examples Github](https://github.com/karamba3d/K3D_Scripting)”-collection which accompanies this manual. A useful additional source of information regarding scripting with Karamba3D are the **“karambaCommon.dll”**- and **“karamba.gha”** files themselves: Feel free to use a tool like **“ILSpy”** to decompile them and have a look at their inner workings.

The main incentive behind decoupling K3D from Grasshopper was to enable Unit-testing for the C# application programming interface (API) of Karamba3D and thus enhance its code quality. You can download the Karamba3D test-project from <https://github.com/karamba3d/K3D_tests>. The test cases represent code-snippets which show how to work with the Karamba3D API and form another useful source of know-how regarding C# of K3D.

Karamba3D is work in progress. So are its interface definitions. Be aware of the fact, that they will probably change in future.


# 1.2: What's New in Karamba3D 1.3.3 Regarding Scripting

## Independence from Grasshopper

The biggest change from Karamba3D 1.3.1 to 1.3.2 is its partial independence from Grasshopper. When taking a look at the contents of the [Karamba3D\_1\_3\_3\_SDKDoc.chm](https://karamba3d.com/help/1-3-3)”-file one sees a list of name-spaces: everything under **“Karamba.GHopper”** comes with **“karamba.gha”** and links the C# API of Karamba3D to GH. All the rest resides in **“karambaCommon.dll”** and can be used independently of Grasshopper and RhinoCommon.dll.

## Karamba3D's Geometry Types

Under **“Karamba.Geometry”** one finds the geometry types of Karamba3D. In most cases the K3D-equivalents of a GH geometry types come without the **“D”** at the end: e.g. Grasshopper’s Vector3D is equivalent to K3D’s Vector3. The static class **“Karamba.GHopper.Utilities.FromGH”** provides typeconversion from GH-wrappes (e.g. GH\_Vector) to K3D types. The namespace **“Karamba.GHopper.Geometry"** contains the classes MeshExtension and MeshExtension which extend the GH and Karamba3D geometry classes with the **“Convert”** method for bi-directional type conversion.

## Factories as Access Points to the Karamba3D API

For nearly all Karamba3D components visible in Grasshopper there is a static class in **“karambaCommon.dll”** which does the work behind the scenes. Thus one approach would be to use these static classes to manage Karamba3D models. However this makes scripts dependent on the interface definitions of these static classes. In order to provide a more stable API a hierarchy of factories has been implemented. It resides under **“KarambaCommon.Factories”**. The **“Toolkit”** class provides access to it and can be used to set up Karamba3D models. An additional advantage of the toolkit consists in the fact that it provides useful default-values for many arguments. Currently not all features of Karamba3D are available via the **“Toolkit”**. It will however grow in future and constitutes the recommended access point to Karamba3D. Take a look at the Karamba3D unit-test project at <https://github.com/karamba3d/K3D_tests> to see how this works.


# 2.1: Hello Karamba3D

For small scripts the Grasshopper scripting components “C# Script”, “VB Script” or “Python Script” from the **“Maths”** subsection come in handy. A good introduction to scripting in GH can be found in the “Grasshopper Primer”.

All examples which follow can be found in the “[Karamba3D Scripting Examples Github](https://github.com/karamba3d/K3D_Scripting)”-collection that accompanies this manual. When opening them in Grasshopper do not panic if some components turn red. In that case some paths need adaptation (see below).

In order to get going let’s start with a simple example (see “**HelloKaramba3D.gh**” in the examples archive): Place a C# scripting component on the canvas, right-click on its icon and select **“Manage Assemblies . . . ”** from the context menu. In order to make the “**karambaCommon.dll**” and “**karamba.gha**” assemblies accessible to your script, click on “Add” left beside “Referenced Assemblies” and browse to the files. They both live in the **“Plug-ins”**-folder of Rhino (e.g. “*C:\ProgramFiles\Rhino6\Plug-ins*”). From there all users can access the Karamba3D plug-in. By default one only sees **“.dll”**-files. Select **“Grasshopper Assemblies (\*.gha)”** from the drop-down list on the lower right side of the browse-window.

The first script (“**HelloKaramba3D.gh**” in the examples folder) takes a K3D model as input and outputs the number of its nodes and elements. Fig. 2.1.1 displays the Grasshopper definition which generates a Karamba3D-model consisting of one beam only. Zooming in on the C#-components makes small **“+”** and **“-”** signs appear next to the input- and output-plugs of the component. Use these to change the number of parameters; right-click on them to change their names. The parameter names which appear on the component act also as the argument names in the underlying C#-script. This sometimes proves to be awkward: input- and output-parameters need to be named differently. Also the fact that input- and output-plugs names normally start with capital letters clashes with customary C# naming conventions where names starting with capital letters normally signify classes.

![Fig. 2.1.1: A minimal K3D-model for retrieving the number of elements, materials and cross sections.](/files/-MXH_8qbowH6_g1a2jDZ)

The source-code to be added inside the C#-component looks like this:

```csharp
    ...
    using Karamba.Models;
    ...
    private void RunScript(object Model_in)
    {
        var model = Model_in as Model;    
        if (model == null) {
            throw new ArgumentException("The input is not of type model!");
      }
      Print("Number of Elements: " + model.elems.Count);
      Print("Number of Materials: " + model.materials.Count);
      Print("Number of Cross sections: " + model.crosecs.Count);
    }
```

The addition of the **“using”** command in line 2 is for convenience. It spares one to type out the fully qualified names of classes. In case of **“Model”** in line 6 this would have been **“Karamba.Models.Model”**. In this example the benefit of the **“using”** command does not weigh much – longer scripts however gain in readability.

**“Model\_in”** comes as type **“object”**, so one needs to cast it into its real type to get access to it. The commands in line 6 do so. Since type conversions may fail (think of a user plugging a vector into where the model should go) the **“if”** in line 7 tests this condition and throws an invalid argument exception if necessary. **“model”** contains now a reference to the model-object and can be used to retrieve data from it. For a complete specification of the API of the **“Model”**-class see “[Karamba3D\_1\_3\_3\_SDKDoc.chm](https://karamba3d.com/help/1-3-3)” where it can be found under **“Karamba.Models”**.

{% file src="/files/EQlaNdRQdi2YZ6krFTRS" %}


# 2.2: Data Retrieval from Models

## The Data Model

The data inside a Karamba3D model is organized in a tree-like object structure. The following diagram shows a part of that tree – for full details see “[Karamba3D\_1\_3\_3\_SDKDoc.chm](https://karamba3d.com/help/1-3-3)”:

![](/files/-MXH_8cizq26hoYB9NSc)

## Retrieving Masses Sorted by Material

The following script (see “**DataRetrieval.gh**”) shows how to use that model-data to retrieve the mass sorted by material. One can see in fig 2.2.1 the corresponding GH setup. The model comprises two beams made from steel and concrete respectively. The total mass amounts to **87.5 kg**, **37.5 kg** come from concrete, **50.0 kg** from steel.

![Figure 2.2.1: Data retrieval from a K3D model.](/files/-MXH_8ckB3AWFBtvo-zl)

```csharp
...
using Karamba.Models;
using Karamba.Utilities;
using Karamba.Materials;
...
private void RunScript(object Model_in)
{
  var model = Model_in as Model;
  if (model == null) {
    throw new ArgumentException("The input is not of type Karamba.Models.Model!");
  }

  var matWeights = new Dictionary<FemMaterial, double>();

  foreach (var elem in model.elems) {
    var mat = elem.crosec.material;
    if (!matWeights.ContainsKey(mat)){
      matWeights[mat] = 0;
    }
    matWeights[mat] += elem.weight(model.nodes);
  }

  var ucf = UnitsConversionFactories.Conv();
  var mass = ucf.force2mass();
  var kg = ucf.kg();

  foreach (var entry in matWeights) {
    Print("Material: " + entry.Key.name + ": " + kg.toUnit(mass.toBase(entry.Value)) + kg.unitB);
  }
}
```

The first lines contain **“using”** statements for the name-spaces **“Karamba.Utilities”** and **“Karamba.Materials”**. These house the **“UnitsConversionFactories”**, **“INIReader”** and **“FemMaterial”**-classes respectively.

The script starts as before with a type-conversion for the argument **“Model\_in”** from **“object”** to **“Model”**. The **“matWeights”** dictionary provides the mapping from K3D materials to weights. A **“foreach”**-loop cycles over all elements of the model and gets their materials. If not already present in **“matWeights”** a new material entry is created. Line 20 updates the material’s total weight.

{% file src="/files/hMghOD9UBN3Jc6UbAzXq" %}

## Handling Physical Units

Creating the output consists of looping over the entries in **“matWeights”**. The tricky part is to get the physical units right. Internally the C++ part of Karamba3D does not care about physical units. As long as they are consistent the results will be fine. When using SI-units Karamba3D works with these base units: meters (**m**), kilo Newtons (**kN**), tons (**t**) and degrees Celsius. When in Imperial-mode the units of length, force and mass get converted to feet (**ft**), kilo Pounds force(**kipf**), kilo Pounds mass (**kipm**) and degrees Fahrenheit. Units conversion between e.g. centimeter and meter, inch and feet, . . . occurs at output and input only. The material and cross section tables which come with Karamba3D – and can be produced via e.g. the **“Generate Cross Section Table”**-component – contain values in SI-units only. They get converted to the right units-system (SI or Imperial) on the fly when loading them into Karamba3D.

The matter of mass has it difficulties: in the SI system there is a clear separation between force (**kN**) and mass (**kg**) and e.g. Newton’s law takes the form:

$$
F\[N] = m\[kg] \* a\[m/s^2]
$$

since the definition holds:

$$
1N = 1kgm/s^2
$$

In Imperial units one has Pound-force (**lb**, sometimes **lbf**) and Pound-mass (**lbm**). The former is defined as the force which corresponds to the weight of one pound mass. So **“g”** – the acceleration of gravity – is not involved. To make Newton’s law work in Imperial units one thus needs to divide the right side by a constant:

$$
g\_c = 32.174 ft/s^2
$$

which is by convention the acceleration of gravity to be used:

$$
F \[lbf] = m\[lbm] · a\[ft/s^2]/g\_c \[ft/s^2]
$$

To make calculations involving mass work irrespective of the system of physical units the **“kg”**- conversion does the following: Under Imperial units masses get scaled by:

$$
1/g\_c
$$

Sometime it happens that one wants to convert weight to mass. In this case weight gets scales by *g\_user/g\_c* and *g\_user* for Imperial and SI-units respectively, g\_user being the acceleration of gravity given in the karamba.ini-file.

The first step in unit-conversion consists of getting a units-conversion-factory (UCF), (see line 23). This factory converts derived SI or Imperial units (e.g. inch, centimeter, millimeter, . . . ) to base units (e.g. feet, meter). A UCF features a long list of conversion objects, “**weight2kg**” being one of them. In line 24 a units conversion object gets instantiated using the factory. This lets one convert from force to mass using the acceleration of gravity from the “karamba.ini”-file via the “**toBase**”-method. Conversion in the other direction works with “**toUnit**”. The method “**unitB**” renders a string representation of the unit with brackets.


# 2.3: How to Create Structural Models

Karamba3D-components are split in two parts: one manages the graphical user interface, unit conversions and default values, the other handles the functionality.

Let’s take the **“LineToBeam”**-component as an example:

* The class **“Component\_LineToBeam\_GUI”** in namespace **“Karamba.GHopper.Elements”** derives from Grasshopper’s **“GH\_Component”** and provides the visual component properties.
* **“LineToBeam”,** a static class in namespace **“Karamba.Elements”** features the static method **“solve(...)”** which executes the actual tasks and gets used by **“Component\_LineToBeam\_GUI”**.

Generally the names of classes which belong to the GUI start with **“Component”** and belong to the namespace **“Karamba.GHopper”**. Since there are static solve-methods for all components it would be possible to build a model using only these. This would entail two disadvantages:

* The solve-methods do not provide default values for their arguments, so one has to provide them explicitly.
* In later versions of Karamba3D the order and number of arguments of the solve-methods might change.

One way to mitigate these problems is to refrain from direct object creation and use a factory-pattern instead. See [\[2\]](/bibliography) for further information on this topic. In the script below a structural model gets assembled and output: it consists of a vertical cantilever-beam with a point-load on top (see fig. 2.3.1).

![Fig. 2.3.1: A model can be created from scratch using a C# script.](/files/-MXH_8YK8Xgq4TshBhlr)

This is the source-code inside the C#-component (see example “**ModelCreation.gh**”):

```csharp
...
using Karamba.Utilities;
using Karamba.Geometry;
using Karamba.CrossSections;
using Karamba.Supports;
using Karamba.Loads;
...
private void RunScript(ref object Model_out)
{
  var logger = new MessageLogger();
  var k3d = new KarambaCommon.Toolkit();

  var p0 = new Point3(0, 0, 0);
  var p1 = new Point3(0, 0, 5);
  var L0 = new Line3(p0, p1);

  var nodes = new List<Point3>();

  var elems = k3d.Part.LineToBeam(new List<Line3>(){L0}, new List<string>(){ "B1" },
    new List<CroSec>(), logger, out nodes);

  var cond = new List<bool>(){ true, true, true, true, true, true};
  var support = k3d.Support.Support(0, cond);
  var supports = new List<Support>(){support};

  var pload = k3d.Load.PointLoad(1, new Vector3(0, 0, -10), new Vector3());
  var ploads = new List<Load>(){pload};

  double mass;
  Point3 cog;
  bool flag;
  string info;
  var model = k3d.Model.AssembleModel(elems, supports, ploads,
    out info, out mass, out cog, out info, out flag);

  // calculate Th.I response
  List<double> max_disp;
  List<double> out_g;
  List<double> out_comp;
  string message;
  model = k3d.Algorithms.AnalyzeThI(model, out max_disp, out out_g, out out_comp, out message);

  var ucf = UnitsConversionFactories.Conv();
  UnitConversion cm = ucf.cm();
  Print("max disp: " + cm.toUnit(max_disp[0]) + cm.unitB);

  Model_out = new Karamba.GHopper.Models.GH_Model(model);
}
```

As a means of reporting problems a **“logger”**-objects gets instantiated in line 10. This class limits the amount of text to a preset maximum so that in case of multiple errors the log-file does not grow without limits. Next comes the factory **“k3d”** which further on serves as the main hub of object creation. The classes **“Point3”** and **“Line3”** represent the Karamba3D equivalent of Grasshopper’s **“Point3d”** and **“Line”**. Their instantiations **“p0”**, **“p1”** and **“L0”** make up the model’s geometry. In line 19 the **k3d-factory** creates a list of objects of type **“BuilderBeam”** – with one entry in this case. This is not yet an element which forms part of a model – this would be **“ModelBeam”**. It rather represents a recipe for creating them. This concept applies to all elements in Karamba3D: Via the assemble-step objects of type **“BuilderBeam”** or **“BuilderShell”** produce **“ModelBeams”**-, **“ModelTruss”**-, **“ModelSpring”** and **“ModelShell”**-objects which form part of the C# structural model. What the user sees as **“Element”** in the Grasshopper GUI are the element-builders not the model-elements. Since C# structural models can be disassembled and reassembled the model-elements need to keep a reference to their builder-elements. This is achieved via the protected property **“builder\_element”**.

The creation of supports and loads works similarly as for the element-builder. In Line 33 follows the model-assemble step, in line 41 the first order theory calculation of the model-response.

User defined objects that get piped through grasshopper definitions need to be wrapped: In line 47 a GH\_Model wrapper object is created from the model-object that contains the final results. Similar wrapper classes exist for all Karamba3D entities that can populate a Grasshopper definition. Their names start with **“GH\_”** which makes them easy to find.

{% file src="/files/72gYsWmAQddnfgZ9DJDL" %}


# 2.4: How to Modify Structural Models

{% content-ref url="/pages/-MXH\_8OKtO9W8PyR39vI" %}
[2.4.1: Cross section Optimization](/2.-scripting/2.4-how-to-modify-structural-models/2.4.1-cross-section-optimization)
{% endcontent-ref %}

{% content-ref url="/pages/-MXH\_8OLycjhQRsWG1uV" %}
[2.4.2: Activation and Deactivation of Elements](/2.-scripting/2.4-how-to-modify-structural-models/2.4.2-activation-and-deactivation-of-elements)
{% endcontent-ref %}


# 2.4.1: Cross section Optimization

When it comes to modifying an existing Karamba3D model keep to these general rules:

* In order to avoid side-effects, clone objects before modifying them. This applies recursively to the objects which contain objects to be modified.
* The Karamba3D API for modifications allows for changes which can make the C++-model crash. So make it a habit to regularly save your work.

The example “**CrossSectionOptimization.gh**” (see fig. 2.4.1.1) shows how to optimize beam cross sections under arbitrary loads. The C#-component takes a model and an ordered list of cross sections as the main input, chooses the optimum cross sections according to the given cross section forces and outputs the optimized model as well as its maximum displacement. The input-plug **“niter”** lets one chose the number of iteration steps. Each consists of model evaluation and cross section selection. With **“lcind”** the index of a load-case to be considered for cross section optimization can be selected. The algorithm inside the component neglects buckling and calculates the maximum stress in the cross section based on the assumption that there are only normal forces and bending moments about the local Y-axis.

![Fig. 2.4.1.1: Modification of cross sections.](/files/-MXH_99VNTWgToVweFas)

```csharp
...
using Karamba.Models;
using Karamba.CrossSections;
using Karamba.Elements;
using Karamba.Results;
...
private void RunScript(object Model_in, List<object> CroSecs_in, int niter, int lcind, ref object Model_out, ref object Disp_out)
{
  var model = Model_in as Model;
  if (model == null) {
    throw new ArgumentException("The input in 'Model_in' is not of type Karamba.Models.Model!");
  }

  var crosecs = new List<CroSec_Beam>(CroSecs_in.Count);
  foreach (var item in CroSecs_in) {
    var crosec = item as CroSec_Beam;
    if (crosec == null) {
      throw new ArgumentException("The input in 'CroSecs_in' contains objects which are not of type Karamba.CrossSections.CroSec_Beam!");
    }
    crosecs.Add(crosec);
  }

  var k3d = new KarambaCommon.Toolkit();
  List<double> max_disp;
  List<double> out_g;
  List<double> out_comp;
  string message;
  List<List<double>> N;
  List<List<double>> V;
  List<List<double>> M;

  // avoid side effects
  model = model.Clone();
  model.cloneElements();

  for (int i = 0; i < niter; ++i) {
    model = k3d.Algorithms.AnalyzeThI(model, out max_disp, out out_g, out out_comp, out message);

    for (int elem_ind = 0; elem_ind < model.elems.Count; ++elem_ind) {
      var beam = model.elems[elem_ind] as ModelBeam;
      if (beam == null) continue;

      // avoid side effects
      beam = (ModelBeam) beam.Clone();
      model.elems[elem_ind] = beam;

      BeamResultantForces.solve(model, new List<string> {"" + elem_ind}, model.lc_combinator.load_case_ids[lcind], 100, 1,
          out N, out V, out M);

      for (int crosec_ind = 0; crosec_ind < crosecs.Count; ++crosec_ind) {
        var crosec = crosecs[crosec_ind];
        beam.crosec = crosec;
        var max_sigma = Math.Abs(N[lcind][0]) / crosec.A + M[lcind][0] / crosec.Wely_z_pos;
        if (max_sigma < crosec.material.ft()) break;
      }
    }

    model.initMaterialCroSecLists();
    model.buildFEModel();
  }

  model = k3d.Algorithms.AnalyzeThI(model, out max_disp, out out_g, out out_comp, out message);

  Disp_out = new GH_Number(max_disp[lcind]);

  Model_out = new Karamba.GHopper.Models.GH_Model(model);
}
```

In the first two paragraphs of source-code the type of the **“Model\_in”** and **“CroSecs\_in”** input gets checked. The third block consists of instantiations of objects which are used later when looping over the model’s elements.

When C# assigns an object to a variable it actually assigns a reference. Changes made on the object are thus visible in all variables that also reference that object. In Grasshopper this breaks the data flow logic that an object is only influenced by upstream operations. Whenever an object gets plugged into two different components a change in one object would magically transfer to the other object. In order to avoid this, the actual data and not only references need to be assigned and copied. This is done in line 33 for the model and in line 36 for its list of elements.

The loop in line 39 cycles over the optimization iterations and starts with calculating the system response. Starting in line 41 all elements get checked whether they epitomize beam-elements. If yes the beam gets cloned (see line 44) to avoid side-effects and re-enlisted in the model.

For cross section-optimization the beam’s resultant cross section forces are determine in line 47. One could have used **“Karamba.Results.BeamForces.solve()”** here instead for considering e.g. bi-axial bending. The loop which follows goes through the list of user-provided cross sections and chooses an appropriate one by comparing the maximum stress in a cross section against the cross section’s material strength.

After completing cross section selection, the iteration step ends with initializing the model’s lists of cross sections and materials (line 58) and recreation of the C++ model on which the C# model depends for result evaluation (line 59).

Before handing over the model to the output-plug an analysis step updates the structural response and determines the model’s maximum displacement.

The above C# script could be improved in many ways i.e. by providing a stop criteria for the optimization iterations or more elaborate design procedures for determining the load-bearing capacity of the elements. Yet this was omitted for the the sake of simplicity.

{% file src="/files/HKLrY5sg0cYtqUd8ex0u" %}


# 2.4.2: Activation and Deactivation of Elements

The example “**ActivationDeactivationofElements.gh**” features a simplified version of Karamba3D’s **“Tension/Compression Eliminator”**-component. It repeatedly evaluates a structure and removes all elements with tensile normal force. In the source-code one finds an alternative approach to changing model properties as compared to the above example. Starting from an arbitrary structural model the script iteratively removes those elements which are under tension (see fig. 2.4.2.1). In order to improve computational efficiency model-modifications occur on the level of the C++ model. This spares the C++ model creation, however introduces additional complexity with regards to model-handling.

![Fig. 2.4.2.1: From the initial truss only those elements without tensile forces survive.](/files/-MXH_8u947z1mPElhI6N)

```csharp
...
using Karamba.Models;
using Karamba.GHopper.Models;
using Karamba.Loads.Combinations;
...
private void RunScript(object Model_in, int maxiter, ref object Model_out, ref object isActive, ref object maxDisp)
{
  var model = Model_in as Model;
  if (model == null) {
    throw new ArgumentException("The input in 'Model_in' is not of type karamba.Models.Model!");
  }

  // load case to consider for elimination of elements
  int lc_num = 0;

  // clone the model and its list of elements to avoid side effects
  model = model.Clone();
  // clone its elements to avoid side effects
  model.cloneElements();
  // clone the feb-model to avoid side effects
  model.deepCloneFEModel();

  string singular_system_msg = "The stiffness matrix of the system is singular.";

  // do the iteration and remove elements with tensile axial forces
  for (int iter = 0; iter < maxiter; iter++) {

    // create a deform and response object for calculating and retrieving results
    feb.Deform deform = new feb.Deform(model.febmodel);
    feb.Response response = new feb.Response(deform);

    try
    {
      // calculate the displacements
      response.updateNodalDisplacements();
      // calculate the member forces
      response.updateMemberForces();
    }
    catch
    {
      // send an error message in case something went wrong
      throw new Exception(singular_system_msg);
    }

    // check the normal force of each element and deactivate those under tension
    double N, V, M;
    bool has_changed = false;
    foreach (Karamba.Elements.ModelElement elem in model.elems) {
      // retrieve resultant cross section forces
      elem.resultantCroSecForces(model,  new LCSuperPosition(lc_num, model), 
        out N, out V, out M);
      // check whether normal force is tensile
      if (N >= 0) {
        // set element inactive
        elem.set_is_active(model, false);
        has_changed = true;
      }
    }

    // leave iteration loop if nothing changed
    if (!has_changed) break;

    // if something changed inform the feb-model about it (otherwise it won't recalculate)
    model.febmodel.touch();

    // this guards the objects from being freed prematurely
    GC.KeepAlive(deform);
    GC.KeepAlive(response);
  }

  // update model to its final state
  try
  {
    // create a deform and response object for calculating and retrieving results
    feb.Deform deform = new feb.Deform(model.febmodel);
    feb.Response response = new feb.Response(deform);

    // calculate the displacements
    response.updateNodalDisplacements();
    // calculate the member forces
    response.updateMemberForces();

    maxDisp = response.maxDisplacement();

    // this guards the objects from being freed prematurely
    GC.KeepAlive(deform);
    GC.KeepAlive(response);
  }
  catch
  {
    // send an error message in case something went wrong
    throw new Exception(singular_system_msg);
  }

  // set up list of true/false values that corresponds to the elemment states
  List<bool> elem_activity = new List<bool>();
  foreach (var elem in model.elems) {
    elem_activity.Add(elem.IsActive);
  }

  isActive = elem_activity;
  Model_out = new GH_Model(model);

  Print("Everything OK");
}
```

As usual at the beginning of the above script the input variable **“Model\_in”** gets type-cast to **“Karamba.Models.Model”**. In case the supplied object does not fit an **“ArgumentException”** gets thrown.

The index of the load case to consider is hard-wired to **“0”** in line 13. In order to avoid side-effects, the data needs to be copied before any modifications take place. Line 16 effects this for the Karamba3D- model. Since the model-object itself contains objects these need to be copied as well. Since the script changes the element’s activation state, the list referencing them needs to be copied as done in line 18. The same is true for the C++-model which gets cloned in line 20.

The C++ model contains all the predefined data like geometry, materials, supports, loads, and the like. It lives in the **“feb”**-namespace which stands for finite element basis. In order to perform an analysis on it, one needs to configure a corresponding object. In line 28 a simple **“Deform”** object gets created which calculates static deflections. A response object lets you query the analysis for results. It gets created in line 29. One has to make sure that a new set of Deform- and Response-objects is created for every new state of the model. This is the reason why their creation occurs inside the loop for changing the activation state of the model elements.

Lines 34 and 36 contain the calls for calculating the model displacements and cross section forces. In case the system can not be evaluated (e.g. due to being kinematic) an exception will fly.

In lines 47 to 56 the algorithm iterates over all elements in the system, reads out their resultant section forces and sets all members inactive that are under tension (*N* ≥*0*). The **“set\_is\_active”** method sets the **“is\_active”**-flag both in the C#-model and the C++-model. If properties get only changed in the C#-model the C++-model gets out of sync and thus would calculate a wrong structure. A way to avoid this would be to rebuild the C++-model completely from the C#-model (as demonstrated in the previous section) after having changed the latter. As this step can be time-consuming it is better avoided whenever possible.

Each time the C++-model changes it needs to be made aware of that. In order to invalidate its state, the **“touch()”**-function gets invoked. When leaving out this command the C++-model will not recalculate in the next iteration as it assumes that its results from the previous cycle are still valid.

As soon as there are no more changes (line 59) or the maximum number of iterations is reached the program leaves the loop and the model gets updated for a last time.

The “**GC.KeepAlive**” commands in lines 84 and 85 make sure that the automatic garbage collection in C# does not prematurely free the **“deform”** and **“response”** objects. As **“deform”** is internally referenced by **“response”** freeing the former object would lead to a dangling pointer and undefined behavior **“response”**.

An iteration over all elements creates a list of boolean values that corresponds to their activation state(lines 94 to 97). In line 99 this list is handed over to the output variable. Line 100 puts a GH\_Model wrapper object around the model-object that contains the final results.

In line 81 the response object is queried for the maximum deflection in the model. Its value is then handed over to the **“maxDisp”** output variable.

{% file src="/files/KXhEzw7t5EaMbx0qjTLs" %}


# 2.5: Data Export from Karamba3D

When it comes to exporting data from a Karamba3D model to another data-format one could simply go through the object tree of the model and iterate manually over the existing entities like e.g. nodes, elements, materials, . . . . The use of a builder pattern removes some of the bureaucratic overhead involved in this approach. The script in the example “**ModelExport.gh**” shows how to generate an XML-file based on a given Karamba3D-model. The output consists of a string which can be streamed to a file. When opened with a web-browser a nicely formatted tree results (see fig. 2.5.1).

![Fig. 2.5.1: ModelExport.gh](/files/-MXH_8mQ8Ka3_HUVaTMk)

By inheriting from **"Karamba.Exporters.ExportBuilder"** and overriding **"builder"**-methods a builder-class can be configured to export Karamba3D-models to any format - here XML.

The corresponding source-code looks like this:

```csharp
...
using System.Xml;
using System.IO;
using Karamba.Models;
using Karamba.Nodes;
using Karamba.CrossSections;
using Karamba.Elements;
using Karamba.Loads;
using Karamba.Materials;
using Karamba.Supports;
using Karamba.Geometry;
...
private void RunScript(object Model_in, ref object XML)
{
    var model = Model_in as Model;
    if (model == null) {
      throw new ArgumentException("The input is not of type model!");
    }

    var builder = new BuilderXML();
    var director = new Karamba.Exporters.ExportDirector();
    director.ConstructExport(model, builder);

    XML = builder.getProduct();
}

// <Custom additional code>
public class BuilderXML : Karamba.Exporters.ExportBuilder {
  // the XML document
  private XmlDocument doc_ = new XmlDocument();
  // the model inside the xml-document
  private XmlElement model_;

  public override void newProduct() {
    model_ = (XmlElement) doc_.AppendChild(doc_.CreateElement("K3DModel"));
  }

  public override void buildMaterial(FemMaterial m, int ind) {
    var xml_node = (XmlElement) model_.AppendChild(doc_.CreateElement("FemMaterial"));
    xml_node.InnerText = "ind: " + ind + ":" + m.ToString();
  }

  public override void buildVertex(Node v) {
    var xml_node = (XmlElement) model_.AppendChild(doc_.CreateElement("Node"));
    xml_node.InnerText = v.ToString();
  }

  public override void buildCroSec(CroSec crosec) {
    var xml_node = (XmlElement) model_.AppendChild(doc_.CreateElement("CroSec"));
    xml_node.InnerText = crosec.ToString();
  }

  public override void buildElement(ModelElement e, Model model) {
    var xml_node = (XmlElement) model_.AppendChild(doc_.CreateElement("ModelElement"));
    xml_node.InnerText = e.ToString();
  }

  public override void buildElementLoad(ModelElement elem, Model model) {
    foreach (var l in elem.Elem_loads) {
      var xml_node = (XmlElement) model_.AppendChild(doc_.CreateElement("ElementLoad"));
      xml_node.InnerText = l.ToString();
    }
  }

  public override void buildLoadCase(int lc_ind, Model model) {
    Vector3 g_vec = new Vector3(0, 0, 0);
    foreach (GravityLoad g in model.gravities.Values) {
      if (model.lc_combinator.lc_inds(g.LcName).Contains(lc_ind)) {
        g_vec = g.force;
        };
      }

    var xml_node = (XmlElement) model_.AppendChild(doc_.CreateElement("LoadCase"));
    xml_node.InnerText = "load-case: " + lc_ind + " g =" + g_vec;

  }

  public override void buildSupport(Support s) {
    var xml_node = (XmlElement) model_.AppendChild(doc_.CreateElement("Support"));
    xml_node.InnerText = s.ToString();
  }

  public override void buildPointLoad(PointLoad p) {
    var xml_node = (XmlElement) model_.AppendChild(doc_.CreateElement("PointLoad"));
    xml_node.InnerText = p.ToString();
  }

  public override void buildMeshLoad(MeshLoad m) {
    var xml_node = (XmlElement) model_.AppendChild(doc_.CreateElement("MeshLoad"));
    xml_node.InnerText = m.ToString();
  }

  public string getProduct() {
    StringWriter sw = new StringWriter();
    XmlTextWriter tx = new XmlTextWriter(sw);
    doc_.WriteTo(tx);
    string str = sw.ToString();//
    return str;
  }
}
// </Custom additional code>
```

The first part of the script comprises the “RunScript”-method. There the “Model\_in”-object gets converted to a Karamba3D-Model. In line 20 follows the creation of a builder-object which does the conversion-work. The class “BuilderXML” gets defined further below in the script. A director-object is instantiated in the next line. Its task consists of iterating over the model constituents and presenting them to the builder. Calling “ConstructExport” in line 22 initiates the creation process. Line 24 assigns the builder’s product to the output variable “XML”.

The “BuilderXML”-class derives from Karamba.Exporters.ExportBuilder and overrides some of its methods. In order to keep things simple each build-method adds a XML-node which contains the string-representation of the corresponding argument. In case of “ModelElement” the corresponding build-method does not know which type of element gets passed. Thus one has to apply pattern matching to find get the right type (e.g. “ModelBeam”, “ModelTruss”, “ModelShell” or “ModelSpring”)

{% file src="/files/F3diOfecMqpijGkmLXIL" %}


# 2.6: The VB Script Component

The steps for setting up a VB script component for using Karamba3D are analogous to those described in section [2.1](/2.-scripting/2.1-hello-karamba3d). In order to run the above example in VB you could use one of the many C# to VB converters (see e.g. [http://www.developerfusion.com/tools/convert/csharp-to-vb/](https://www.developerfusion.com/tools/convert/csharp-to-vb/)) to get the corresponding VB source text.


# 2.7: The IronPython Component

IronPython is the DotNet version of Python. Being an open platform independent scripting language, Python comes with many useful libraries (of which however not all run under IronPython).

In order to use it from within Grasshopper under Rhino5 install GHPython which can be downloaded from [Food4Rhino](https://www.food4rhino.com/app/ghpython).

Grasshopper for Rhino6 and 7 comes with GHPython by default.

{% content-ref url="/pages/-MXH\_8OPHx5jsPoTq9B\_" %}
[2.7.1: Results Retrieval on Shells](/2.-scripting/2.7-the-ironpython-component/2.7.1-results-retrieval-on-shells)
{% endcontent-ref %}

{% content-ref url="/pages/-MXH\_8OQR8ZuIhMNpa3m" %}
[2.7.2: A Simplified ESO-Procedure on Shells](/2.-scripting/2.7-the-ironpython-component/2.7.2-a-simplified-eso-procedure-on-shells)
{% endcontent-ref %}


# 2.7.1: Results Retrieval on Shells

The example file “**SimpleShellESO.gh**” (see fig. 2.7.1.1) contains two Python-scripts which will be explained below. The first script retrieves results from Karamba3D’s triangular shell elements:

![Fig. 2.7.1.1: "SimpleShellESO"](/files/-MXH_8WYq_ub2wIxEkXc)

Two python scripts: the first retrieves shell results (see code block below), the second one performs a simplified variant of an evolutionary structural optimization (ESO) procedure (see section [2.7.2](/2.-scripting/2.7-the-ironpython-component/2.7.2-a-simplified-eso-procedure-on-shells)).

```python
import clr

clr.AddReferenceToFileAndPath("C:\Program Files\Rhino 6\Plug-ins\Karamba\Karamba.gha")
clr.AddReferenceToFileAndPath("C:\Program Files\Rhino 6\Plug-ins\Karamba\KarambaCommon.dll")

import Karamba.Models.Model as Model
import Karamba.Elements.ModelShell as Shell
import feb.ShellMesh as ShellMesh
import feb.TriShell3D as TriShell
import feb.VectSurface3DSigEps as TriStates
import feb.EnergyVisitor as EnergyVisitor

for element in Model_in.elems:
   if type(element) != Shell:
       continue

   print "shell!"
   femesh = Model_in.febmodel.triMesh(element.fe_id)
   n_tri = femesh.numberOfElems()
   print "number of triangle elements:", n_tri

   energy_visitor = EnergyVisitor(Model_in.febmodel, Model_in.febmodel.state(0), 0);
   energy_visitor.visit(Model_in.febmodel);

   for ind in xrange(n_tri):
       print "Axial Energy ", energy_visitor.axialEnergy(Model_in.elems[0].fe_id + ind)
       print "Bending Energy ", energy_visitor.bendingEnergy(Model_in.elems[0].fe_id + ind)

   tri_states = tri_states = femesh.elementSigEps(Model_in.febmodel, 0, Model_in.lc_super_model_displacements)
   for tri_state in tri_states:
       s = tri_state.sig_princ()
       print "first principal stress (kN/cm2):",s.x()/10000
       print "second principal stress (kN/cm2):",s.y()/10000

print "Number of elements", Model_in.elems.Count
```

Lines 3 and 4 reference the Karamba3D DotNet-assemblies. Depending on how you installed Rhino it might be necessary to adapt the paths. In case that one of them can not be located an error will be issued.

Plug the output of **“out”** into a panel. If there is only one line of output, type **“GrasshopperDeveloperSettings”** in the Rhino text window and check whether the **“Memory load \*.GHA assemblies using COFF byte arrays”**-option is unhooked.

The retrieval of axial- and bending-energies works via the visitor-pattern (see \[[2](/bibliography)] for details on that). Line 22 creates such a visitor object for the elastic element energies by handing over a reference to a C++ model, a state, a load-case index and possibly a load-case factor. Unless one performs non-linear calculations state **“0”** is the right one to chose.

A shell patch consists of several shell elements. This might lead to some confusion since in the Grasshopper UI shell patches are named **“elements”**, in the C++ model however the patches consist of several triangular shell elements. In lines 26 and 27 the property **“fe\_ind”** returns the index of a C++ element that corresponds to a given C#-element. In case of shell-patches this corresponds to the first C++-element.

Other shell results like principal stresses can be retrieved directly from the FE-mesh like in lines 29 to 33. **“Model\_in.superimpFacsStates”** represents a list of load-factors which gets defined via the **“Result-Case”**-setting or the input-plug **“R-Factors”** of the **“ModelView”**-component.

{% file src="/files/VvoWpim4HOo2YZGxNPlk" %}


# 2.7.2: A Simplified ESO-Procedure on Shells

The example file “**SimpleShellEso.gh**” contains also a Python script which performs a simple evolutionary structural optimization (ESO) procedure on shell elements. As it applies no filters for calculating the fitness of individual shell triangles checkerboard patterns result (see fig. 2.7.2.1). Alas the script can be easily extended to include more elaborate fitness calculation schemes.

![Fig. 2.7.2.1: SimpleShellEso.gh](/files/-MXH_8WYq_ub2wIxEkXc)

The script shows how to work directly with the C++ model in order to avoid costly mappings to and from the C#-model:

```python
import clr

clr.AddReferenceToFileAndPath("C:\Program Files\Rhino 6\Plug-ins\Karamba\Karamba.gha")
clr.AddReferenceToFileAndPath("C:\Program Files\Rhino 6\Plug-ins\Karamba\KarambaCommon.dll")

import Karamba.Models.Model as Model
import Karamba.Elements.ModelShell as Shell
import Karamba.Materials.FemMaterial_Isotrop as FemMaterial
import feb.ShellMesh as ShellMesh
import feb.TriShell3D as TriShell3D
import feb.VectSurface3DSigEps as TriStates
import feb.Deform as Deform
import feb.Response as Response
import feb.EnergyVisitor as EnergyVisitor
import Rhino.Geometry as Rh

from operator import attrgetter

# encapsulate ESO properties of shell elements
class EsoItem:
    def __init__(self, shell_elem, elem_ind):
       self.active = True
       self.fitness = 0
       self.shell_elem = shell_elem
       self.area = shell_elem.area()
       self.ind = elem_ind

    def update(self, energy_visitor):
       self.fitness = energy_visitor.elasticEnergy(self.ind) / self.area


# clone model to avoid side effects
model = Model_in.Clone()
model.deepCloneFEModel()

# generate ESO properties of each triangular shell element
eso_items = []
for elem in model.elems:
    if type(elem) != Shell:
       continue
    tri_mesh = model.febmodel.triMesh(elem.fe_id)
    for i in xrange(tri_mesh.numberOfElems()):
        eso_items.append(EsoItem(tri_mesh.elem(i), i))

nremove_per_iter = int(NRemove/NIter+1)
n_removed = 0

# do the ESO iterations
for iter in xrange(NIter):
    analysis = Deform(model.febmodel)
    response = Response(analysis)

    try:
        response.updateNodalDisplacements()
        response.updateMemberForces()
    except:
        raise Exception("singular stiffness")

    energy_visitor = EnergyVisitor(model.febmodel, model.febmodel.state(0), 0);
    energy_visitor.visit(model.febmodel);

    for eso_item in eso_items:
        eso_item.update(energy_visitor)

    eso_items = sorted(eso_items, key = attrgetter("fitness"))

    n_removed_per_iter = 0
    has_changed = False
    for eso_item in eso_items:
        if (n_removed >= NRemove): break
        if (n_removed_per_iter >= nremove_per_iter): break
        if (eso_item.active == False):
            continue
        eso_item.shell_elem.softKilled(True)
        eso_item.active = False
        n_removed +=1
        n_removed_per_iter +=1

    has_changed = True
    if (has_changed == False):
        break
    model.febmodel.touch()

    # create active and inactive mesh for output
    active_mesh = Rh.Mesh()
    inactive_mesh = Rh.Mesh()
    for i in xrange(model.febmodel.numberOfNodes()):
        feb_pos = model.febmodel.node(i).pos()
        active_mesh.Vertices.Add(Rh.Point3d(feb_pos.x(), feb_pos.y(), feb_pos.z()))
        inactive_mesh.Vertices.Add(Rh.Point3d(feb_pos.x(), feb_pos.y(), feb_pos.z()))

    for eso_item in eso_items:
        ind0 = eso_item.shell_elem.node(0).ind()
        ind1 = eso_item.shell_elem.node(1).ind()
        ind2 = eso_item.shell_elem.node(2).ind()
        if (eso_item.active):
            active_mesh.Faces.AddFace(Rh.MeshFace(ind0, ind1, ind2))
        else:
            inactive_mesh.Faces.AddFace(Rh.MeshFace(ind0, ind1, ind2))

    activeMesh = active_mesh
    inactiveMesh = inactive_mesh
```

In the above code the class **“ESOItem”** handles the book-keeping necessary in the optimization steps. it contains the activation-state, the fitness, the element’s area, a reference to the C++-element and the element’s index in the C#-model. The **“update”**-method calculates the specific elastic energy of the underlying shell-element.

Activation and deactivation of model elements works via setting the soft-kill status of C++-elements to **“True”** or **“False”** (see line 74). On model-assembly the stiffness of the corresponding element will be multiplied with the soft-kill-factor which is **1.0×10^−10**. This factor can be set on the C++-model via **“softKillFactor(new\_factor)”** if necessary.

The last part of the script categorizes the shell-faces into active or in-active adding their geometry to the corresponding output-meshes.

{% file src="/files/VvoWpim4HOo2YZGxNPlk" %}


# 3.1: Setting up a Visual Studio Project for GH Plug-ins

In case of larger scripting projects advanced debugging facilities and the organization of source code in neatly separated files makes life easier. Integrated development environments like Microsoft Visual Studio offer these possibilities – and some more. The **“Community”**-version of Visual Studio can be downloaded for free from the Microsoft web-site.

A useful GH related tool for Visual Studio can be found at: <https://marketplace.visualstudio.com/items?itemName=McNeel.GrasshopperAssemblyforv6>

It contains project and component wizards which take care of the project settings and boiler-plate code necessary to create valid GH-components.


# 3.2: Basic Component Setup

The example in this section assumes that you have Visual Studio 2017 and the above mentioned GH add-on installed. In order to make things easy, the script will be analogous to that presented in section [2.4.2](/2.-scripting/2.4-how-to-modify-structural-models/2.4.2-activation-and-deactivation-of-elements). Follow these steps to set up a project for creating a GH component which makes use of Karamba3D functionality:

1. Start Visual Studio and select **“File/New/Project . . . ”** from the main menu. A Window appears which lets you select from among different project types (see fig 3.2.1).
2. Go to the bottom of the window and set the name of the project to **“TensionElim”**.

   **Grasshopper loads plug-ins according to the alphanumeric order of their file-names. If a component intends to use Karamba3D functionality make sure its name comes after “Karamba3D” otherwise it will not be able to reference it.**
3. Browse to a folder where your project shall be created using the **“Browse”**-button
4. Unhook the **“Create directory for solution”**-option.
5. Select “Visual C#” from the right hand side tree-menu of installed templates. In case you want to script in VB select the corresponding entry. Double-click on **“Grasshopper Add-On”** in the middle part of the window (see fig. 3.2.1).
6. Another window appears that lets you configure the properties of the Grasshopper component.
7. You can now change the name, nick-name, category (e.g. **“Karamba3D”**) and subcategory (e.g. **“Extra”**) under which the component appears inside Grasshopper – but they can also be changed later on.
8. In case that the path to Grasshopper, RhinoCommon or Rhino appears in red click on the “...” button and point to the right directory.
9. Press **“Finish”**. The GH component wizard now creates the project **“TensionElim”** which already contains the basic frame for a GH component.

![Fig. 3.2.1: Select Grasshopper Add-On from the templates available for C# or VB.](/files/-MXH_8fhMgDALglovQs3)

Select **“View/SolutionExplorer”** to get a list of all files that belong to the project. The file **“Assem- blyInfo.cs”** in the **“Properties”**-folder of the SolutionExplorer lets you set the title of your assembly, a description, a copyright message and so on. The folder **“References”** lists all those assemblies which the component borrows functionality from. By default these are **“GH\_IO”**, **“Grasshopper”**, **“RhinoCom- mon”** and some system assemblies. In **“TensionElimComponent.cs”** you will find the definition of the class **“TensionElimComponent”**. It inherits its functionality from the class **“GH\_Component”** and thus lets you define the properties and behavior of the component later visible in GH.

As a first try right-click on the project **“TensionElim”** and select **“Build”** from the context menu. If all goes well the file **“TensionElim.gha”** gets created in the **“bin”**-folder of the Visual Studio project. Copy this file to one of the places where Grasshopper looks for plug-ins at start-up. Among others these two choices exist:

* The **“Libraries”**-folder of Grasshopper. It sits in the user-directors under **“C:/Users/Username/App- Data/Roaming/Grasshopper/Libraries”** an can be accessed from within Grasshopper via the menu **“File/Special Folders/Components Folder”**. The advantage of putting a plug-in there is, that a user does not need any special privileges to install it.&#x20;
* The **“Plug-ins”**-folder of Rhino to be found at e.g. **“C:/Program Files/Rhino6”** when working with Rhinoceros 6. Placing a plug-in there makes it accessible to all users of the computer. The disadvantage of this file location lies in the fact that one needs to have admin-rights to install a file there.

Karamba3D lives in the **“Plug-ins”**-folder of Rhino. So plug-ins which reference its functionality need to be placed there as well.

In order to avoid the manual copying of the **“.gha”**-file you can setup a post-build-event in the Visual Studio Project settings – this makes debugging more comfortable: Right-click on the project entry in the Solution Explorer, select **“Properties”**, choose **“Build Events”** from the left-hand tabs, and add in one line at the end of the **“Post-build event command line”** text window:

`Copy ’’$(TargetDir)$(ProjectName).gha’’`  \
`’’C:\Users\YourUserNameHere!!\App Data\Roaming\Grasshopper\Libraries\$(ProjectName).gha’’`

or

`Copy ’’$(TargetDir)$(ProjectName).gha’’`  \
`’’C:\ProgramFiles\Rhino6\$(ProjectName).gha’’`

In case you use Rhino 6. Do not forget to replace **“YourUserNameHere!!”** by your user name in case of option one. For option one has to assume ownership of of the Rhino **“Plug-ins”**-folder and acquire write-rights otherwise the copy command fails.

Upon starting (or restarting) GH there should now show up a new icon in the category and subcategory previously provided by you in the constructor of the **“TensionElimComponent”**-class (see fig. 3.2.2). As yet there is no image attached to the new button. Therefore it shows up as a circle filled with black and white rectangles. Take a look at the function **“Icon”** of the **“TensionElimComponent”**- class to change this if you want. You will also notice that the new component has neither input- nor output-plugs.

![Fig. 3.2.2: First step: Custom component without input- or output-plugs.](/files/-MXH_8fjskXRg0xjJw7l)


# 3.3: How to Reference Karamba3D Assemblies

In order to package the script of section [2.4.2](/2.-scripting/2.4-how-to-modify-structural-models/2.4.2-activation-and-deactivation-of-elements) into a GH-component there needs to be one input-plug that receives a Karamba3D model. Three output plugs return an updated Karamba3D model, a list of boolean values that signifies which elements are active or not and the value of the largest resultant displacement.

The following example can be found in the examples that accompany this guide. Go to **“#/TensionElim”** and double-click on **“TensionElim.sln”** to open the project with Visual Studio. Depending on what version of Grasshopper you use it will be necessary to reestablish the project references.

The first step consists in referencing the **“karambaCommon.dll”**-library so that Karamba3D-classes (such as the Model-class) can be dealt with. For this right-click on **“References”** in the **“Solution Explorer”** select **“Add Reference...”** from the context menu and go to **“Browse”** on the tabbed view and point to **“karambaCommon.dll”** in the Rhino **“Plug-ins”**-folder. Since Karmba3D shall be used within Grasshopper, one needs to include **“karamba.gha”** as well. Alas by default Visual Studio does not allow the selection of files with extension **“.gha”**.

As a work-around do the following: Save and close your Visual Studio project. In the project directory you will find a file called **“TensionElim.csproj”**. Open it with a text editor, search for **“karambaCommon”**, copy its entry, fill in **“karamba.gha”** (see fig. 3.3.1), save it and reopen the Visual Studio project.

Now right-click on the **“karamba”** and **“karambaCommon”**-entries in **“References”** and select **“Properties”** from the context menu. Set the property **“Copy Local”** (it has the value true by default) to false.

![Fig. 3.3.1: Work-around for referencing "karamba.gha" in Visual Studio: Edit the "TensionElim.csproj"-file](/files/-MXH_8rRzU7AbedXEH2W)


# 3.4: Input- and Output-Plugs

Now it is time to add the input- and output-plugs to the new component. This is the listing of the first few lines of **“TensionElimComponent.cs”** which implements this functionality:

```csharp
using System;
using System.Collections.Generic;

using Grasshopper.Kernel;

using Karamba.Models;
using Karamba.GHopper.Models;
using Karamba.Loads.Combinations;

namespace TensionElim {
    public class TensionElimComponent : GH_Component
    {
        public TensionElimComponent()
            : base("TensionElim", "TenElim",
                ".", "Karamba" , "Extra" )
                {
                }

                protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager)
                {
                    pManager.AddParameter(new Param_Model(), "Model_in", "Model_in",
                        "Model to be manipulated", GH_ParamAccess.item);
                }

                protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager)
                {
                    pManager.RegisterParam(new Param_Model(), "Model_out", "Model_out",
                        "Model after eliminating all tension elements");
                    pManager.Register_BooleanParam("isActive", "isActive",
                        "List of boolean values corresponding to each element in the model." +
                        "True if the element is active.");
                    pManager.Register_NumberParam("maximum displacement", "maxDisp",
                        " Maximum displacement [m] of the model after eliminationprocess.");
                }
        ...
    }
```

Line 1 to 4 get automatically created by the GH-wizard. Lines 6 and 7 are important: they make the classes available (i.e. Model, Param\_Model, GH\_Model, Element, ...) which reside in the namespaces **“Karamba.Models”** and **“Karamba.GHopper.Models”**. This allows to define the component input in lines 20 and 21. Objects that are used as component input or output need to be wrapped so that GH can handle them. In Karamba3D these wrapper classes are named after the class they wrap preceded by **“Param\_”** and **“GH\_”**. Lines 26 to 32 specify the output plugs. Supplement **“TensionElim- Component.cs”** with the above lines, compile the project and copy the resulting **“TensionElim.gha”** as before. Restart Rhino.\
Fig. 3.4.1 shows the component with input and output-plugs.

![Fig. 3.4.1: Second step: Custom component with input- and output-plugs but no functionality yet.](/files/-MXH_8ocRinDF5bDk5dM)


# 3.5: Adding Functionality to a GH Component

In order to make the component do something one has to add code to the **“SolveInstance”** function in **“TensionElimComponents”**:

```csharp
protected override void SolveInstance(IGH_DataAccess DA)
{
    GH_Model in_gh_model = null;
    if (!DA.GetData<GH_Model>(0, ref in_gh_model)) return;
    var model = in_gh_model.Value;

    // maximum number of iterations
    int max_iter = 10;
    DA.GetData<int>(1, ref max_iter);

    // load case to consider for elimination of elements
    int lc_num = 0;

    // clone model to avoid side effects
    model = (Karamba.Models.Model)model.Clone();

    // clone its elements to avoid side effects
    model.cloneElements();

    // clone the feb-model to avoid side effects
    model.deepCloneFEModel();

    string singular_system_msg = "The stiffness matrix of the system is singular.";

    // do the iteration and remove elements with tensile axial forces
    for (int iter = 0; iter < max_iter; iter++)
    {
        // create an analysis and response object for calculating and retrieving results
        feb.Deform analysis = new feb.Deform(model.febmodel);
        feb.Response response = new feb.Response(analysis);

        try
        {
            // calculate the displacements
            response.updateNodalDisplacements();
            // calculate the member forces
            response.updateMemberForces();
        }
        catch
        {
            // send an error message in case something went wrong
            throw new Exception(singular_system_msg);
        }

        // check the normal force of each element and deactivate those under tension
        double N, V, M;
        bool has_changed = false;
        foreach (var elem in model.elems)
        {
            // retrieve resultant cross section forces
            elem.resultantCroSecForces(model,  new LCSuperPosition(lc_num, model), 
                out N, out V, out M);
            // check whether normal force is tensile
            if (!(N >= 0)) continue;
            // set element inactive
            elem.set_is_active(model, false);
            has_changed = true;
        }

        // leave iteration loop if nothing changed
        if (!has_changed) break;

        // if something changed inform the feb-model about it (otherwise it won't recalculate)
        model.febmodel.touch();

        // this guards the objects from being freed prematurely
        GC.KeepAlive(analysis);
        GC.KeepAlive(response);
    }

    // update model to its final state
    double max_disp = 0;
    try
    {
        // create an analysis and response object for calculating and retrieving results
        feb.Deform analysis = new feb.Deform(model.febmodel);
        feb.Response response = new feb.Response(analysis);

        // calculate the displacements
        response.updateNodalDisplacements();
        // calculate the member forces
        response.updateMemberForces();

        max_disp = response.maxDisplacement();

        // this guards the objects from being freed prematurely
        GC.KeepAlive(analysis);
        GC.KeepAlive(response);
    }
    catch
    {
        // send an error message in case something went wrong
        throw new Exception(singular_system_msg);
    }

    // set up list of true/false values that corresponds to the element states
    List<bool> elem_activity = new List<bool>();
    foreach (var elem in model.elems)
    {
        elem_activity.Add(elem.IsActive);
    }

    DA.SetData(0, new GH_Model(model));
    DA.SetDataList(1, elem_activity);
    DA.SetData(2, max_disp);
}
```

In line 3 a wrapper-object for a Karamba3D model gets initialized to null and set to the value of the input plug of index **“0”**. The **“if”** statement in line 4 checks whether there is any data to process. In line 5 the Karamba3D-model gets retrieved from the GH-wrapper. In lines 8 to 9 the value of the **“maxiter”** plug-in gets read.

All the rest down to line 101 constitutes more or less a replica of the code of section [2.4.2](/2.-scripting/2.4-how-to-modify-structural-models/2.4.2-activation-and-deactivation-of-elements). Lines 102 to 104 transfer the results of the algorithm to the output-plugs.

The example **“ActivationDeactivationOfElements\_CustomComponent”** (see fig. 3.5.1) shows that the results using the GH custom component are the same as in section [2.4.2](/2.-scripting/2.4-how-to-modify-structural-models/2.4.2-activation-and-deactivation-of-elements).

A comparison with the listing in section [2](/2.-scripting/2.1-hello-karamba3d) shows that only the first and last parts differ significantly:

![Fig. 3.5.1: Elimination of elements under tension. this time using a custom GH-component.](/files/-MXH_8myEJNQ7wI6gcCZ)

Visual Studio Project File:

{% file src="/files/n6OZsVY21rdyV7ohe7sH" %}


# Bibliography

|      |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| ---- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| \[1] | Joseph Albahari. C# 7.0 in a Nutshell. O’Reilly UK Ltd., 2017. ISBN 1.491987650. URL [https://www.ebook.de/de/product/29084295/joseph\_albahari\_c\_7\_0\_in\_a\_ nutshell.html](https://www.oreilly.com/library/view/c-70-in/9781491987643/).                                                                                                                                                                                                                                                                                                                                                                                         |
| \[2] | Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides. Design Pat- terns: Elements of Reusable Object-Oriented Software. Addison-Wesley Professional, 199.4. ISBN 8601.4190.477.41. URL [https://www.amazon.com/ Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612? SubscriptionId=AKIAIOBINVZYXZQZ2U3A\&tag=chimbori05-20\&linkCode=xm2\&camp=2025\&creative=165953\&creativeASIN=0201633612](https://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612?SubscriptionId=AKIAIOBINVZYXZQZ2U3A\&tag=chimbori05-20\&linkCode=xm2\&camp=2025\&creative=165953\&creativeASIN=0201633612). |
| \[3] | Mark Michaelis. Essential C# 7.0. Microsoft Press, 2018. ISBN 1509303588. URL <https://www.ebook.de/de/product/28341699/mark_michaelis_essential_c_7_0.html>.                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| \[4] | Jon Skeet. C# in Depth. Manning, 2019. ISBN 161729.4535. URL [https://www.ebook.de/de/ product/30504497/jon\_skeet\_c\_in\_depth.html](https://www.ebook.de/de/product/30504497/jon_skeet_c_in_depth.html).                                                                                                                                                                                                                                                                                                                                                                                                                            |


