Trouble casting [A] to [B] in C#

Hi all,

I’m trying to work with Alberic Trancart’s script for converting a Karamba-model to a .dat file (readable by Sofistik).
The original code is a bit out-dated, as it was last updated in 2014, and I was trying to make it work for the newer Karamba build.

However, I get the following error-message:

[A]Grasshopper.Kernel.Data.GH_Structure`1[Karamba.Models.GH_Model] cannot be cast to 
[B]Grasshopper.Kernel.Data.GH_Structure`1[Karamba.Models.GH_Model]. 

Type A originates from 'Grasshopper, Version=1.0.0.20, Culture=neutral, PublicKeyToken=dda4f5ec2cd80803' in the context 'LoadFrom' at location 
'C:\Program Files\Common Files\McNeel\Rhinoceros\5.0\Plug-ins\Grasshopper (b45a29b1-4343-4035-989e-044e8580d9cf)\0.9.76.0\Grasshopper.dll'. 

Type B originates from 'Grasshopper, Version=1.0.0.20, Culture=neutral, PublicKeyToken=dda4f5ec2cd80803' in the context 'LoadFrom' at location 
'C:\Program Files\Common Files\McNeel\Rhinoceros\5.0\Plug-ins\Grasshopper (b45a29b1-4343-4035-989e-044e8580d9cf)\0.9.76.0\Grasshopper.dll'.

Somehow it seems I’m trying to cast from and to the exact same location.
Does anyone have an idea what’s going wrong?

Please note that C# is quite new to me.

Kind regards,

Merijn

Could it be that you’re loading that assembly twice, from two different locations? If you search your entire computer for all instances of that dll or gha file, how many hits do you get?

Another reason could be that the COFF loading is giving you grief. Try GrasshopperDeveloperSettings and switch COFF loading off. Then restart Rhino and Grasshopper and see if that makes a difference.

1 Like

Hey thanks for the reply!
I tried this already, the problem maintained an the extra problem occured that the Karamba “line-to-beam”-component didn’t work anymore…

Hi David,

I do find two instances of the grasshopper.dll.
One in “C:\Users\907623.nuget\packages\grasshopper\0.9.76\lib\net35”
and one in the usual common files “C:\Program Files\Common Files\McNeel\Rhinoceros\5.0\Plug-ins\Grasshopper (b45a29b1-4343-4035-989e-044e8580d9cf)\0.9.76.0”

If I check the references in visual studio, I see the reference to grasshopper.dll once with one path.
image
image

Deleting the .nuget folder doesn’t work either…

Maybe I am wrong, but had a similar issue when I had copy local to true as I see you have. That makes a copy of the reference.

Ah, thought I already turned that to false… I see you’re right.
I’ll give that a go and report back in tomorrow!

Set all the copy local to false, deleted the duplicate instances of the .dll, problem still occurs…
I guess there’s something wrong in the code? This is the code where I guess something would be wrong then:

// Karamba To Sofistik component for GrassHopper
// Convert a karamba model to a .dat file readable by Sofistik
// Git: https://github.com/AlbericTrancart/karambaToSofistik
// Contact: alberic.trancart@eleves.enpc.fr

using System;
using System.Collections.Generic;
using System.IO;

using Grasshopper.Kernel;
using Rhino.Geometry;

using Karamba.Models;
using Karamba.Elements;

using karambaToSofistik.Classes;

namespace karambaToSofistik {
    public class karambaToSofistikComponent : GH_Component {
        // Component configuration
        public karambaToSofistikComponent() : base("karambaToSofistik", "ktS", "Converts a Karamba model to a .dat file readable by Sofistik", "Karamba", "Extra") { }

        // Registers all the input parameters for this component.
        protected override void RegisterInputParams(GH_Component.GH_InputParamManager pManager) {
            pManager.AddParameter(new Param_Model(), "Model", "Model", "Model to convert", GH_ParamAccess.item);
            pManager.AddTextParameter("Path", "Path", "Save the .dat file to this path", GH_ParamAccess.item, @"");
        }

        // Registers all the output parameters for this component.
        protected override void RegisterOutputParams(GH_Component.GH_OutputParamManager pManager) {
            pManager.Register_StringParam("Output", "Output", "Converted model");
            pManager.Register_StringParam("Status", "Status", "Errors or success messages");
        }

        // We need to register all groups defined in Grasshopper
        static public List<string> beam_groups = new List<string>();

        // This is the method that actually does the work.
        protected override void SolveInstance(IGH_DataAccess DA) {
            // Some variables
            string output = "";                        // The file output
            string status = "Starting component...\n"; // The debug output

            // Several arrays where the data is stored
            List<Material> materials = new List<Material>();
            List<CrossSection> crossSections = new List<CrossSection>();
            List<Node> nodes = new List<Node>();
            List<Beam> beams = new List<Beam>();
            List<Load> loads = new List<Load>();

            // We need to reset some variables because the objects are not freed until Grasshopper is unloaded
            Parser.id_count = 1;

            try {
                // Load the data from Karamba

                // Retrieve and clone the input model
                GH_Model in_gh_model = null;
                if (!DA.GetData<GH_Model>(0, ref in_gh_model)) return;
                Model model = in_gh_model.Value;
                model = (Karamba.Models.Model) model.Clone(); // If the model is not cloned a modification to this variable will imply modification of the input model, thus modifying behavior in other components.

                if (model == null) {
                    status += "ERROR: The input model is null.";
                    output = "Nothing to convert";
                }
                else {
                    string path = null;
                    if (!DA.GetData<string>(1, ref path)) { path = ""; }
                    if (path == "") {
                        status += "No file path specified. Will not save data to a .dat file.\n";
                    }



                    // Retrieve and store the data

                    // Materials
                    foreach (Karamba.Materials.FemMaterial material in model.materials) {
                        // The first material seems to be wong but I don't know why it exists
                        if(model.materials.IndexOf(material) != 0)
                            materials.Add(new Material(material));
                    }

                    /*Disabled for forward compatibility
                    // Check for material duplicates
                    // This is necessary because karamba uses a preset material that is added every time that a model is assembled
                    // As a consequence a model can get a great amount of redundant materials that will flood the output 

                    // Furthermore karamba seems to create a buggy material at index 0 during the cloning operation
                    materials.RemoveAt(0);
                    // Using a for loop because a collection used in foreach is immutable
                    for (int i = 0; i < materials.Count; i++) {
                        materials.RemoveAll(delegate(Material test_material) {
                            return test_material.id != materials[i].id && materials[i].duplicate(test_material);
                        });
                    }
                    */
                    status += materials.Count + " materials loaded...\n";



                    // Cross sections
                    foreach (Karamba.CrossSections.CroSec crosec in model.crosecs) {
                        crossSections.Add(new CrossSection(crosec));
                    }
                    /*Disabled for forward compatibility
                    // The same happens with Cross Sections
                    crossSections.RemoveAt(0);
                    for (int i = 0; i < crossSections.Count; i++) {
                        crossSections.RemoveAll(delegate(CrossSection test_crosec) {
                            return test_crosec.id != crossSections[i].id && crossSections[i].duplicate(test_crosec);
                        });
                    }
                    status += crossSections.Count + " cross sections loaded...\n";
                    */


                    // Nodes
                    foreach (Karamba.Nodes.Node node in model.nodes) {
                        nodes.Add(new Node(node));
                    }
                    status += nodes.Count + " nodes loaded...\n";

                    foreach (Karamba.Supports.Support support in model.supports) {
                        nodes[support.node_ind].addConstraint(support);
                    }
                    status += "Support constraints added to " + model.supports.Count + " nodes.\n";



                    // Beams
                    foreach (Karamba.Elements.ModelElement beam in model.elems) {
                        Beam curBeam = new Beam(beam);

                        // Adding the start and end nodes
                        curBeam.start = nodes[curBeam.ids[0]];
                        curBeam.end = nodes[curBeam.ids[1]];
                        beams.Add(curBeam);
                    }
                    status += beams.Count + " beams loaded...\n";



                    // Loads
                    foreach (KeyValuePair<int, Karamba.Loads.GravityLoad> load in model.gravities) {
                        loads.Add(new Load(load));
                    }
                    status += model.gravities.Count + " gravity loads added.\n";
                    
                    foreach (Karamba.Loads.PointLoad load in model.ploads) {
                        Load current = new Load(load);
                        current.node = nodes[load.node_ind];
                        loads.Add(current);
                    }
                    status += model.ploads.Count + " point loads added.\n";
                    
                    foreach (Karamba.Loads.ElementLoad load in model.eloads) {
                        // Create a load variable base on the load type
                        Load current = new Load();

                        Karamba.Loads.UniformlyDistLoad line = load as Karamba.Loads.UniformlyDistLoad;
                        Karamba.Loads.StrainLoad pret = load as Karamba.Loads.StrainLoad;
                        Karamba.Loads.TemperatureLoad temp = load as Karamba.Loads.TemperatureLoad;
                                
                        if (line != null) {
                            current = new Load(line);
                        }
                        // Very important to check Temperature BEFORE Pretension becaus Temperature derivates from Pretension
                        else if (temp != null) {
                            current = new Load(temp);
                        }
                        else if (pret != null) {
                            current = new Load(pret);
                        }
                        

                        // If there is not target element, apply the load to the whole structure
                        if (load.beamId == "") {
                            current.beam_id = "";
                            loads.Add(current);
                        }
                        else {
                            // We search the element
                            current.beam = beams.Find(delegate(Beam beam) {
                                return beam.user_id == load.beamId;
                            });
                            loads.Add(current);
                        }
                    }

                    status += model.eloads.Count + " line loads added.\n";

                    // ID matching
                    // Karamba and Sofistik use different ID systems
                    // Karamba's materials and cross sections are pointing to an element ID
                    // Sofistik's elements need a cross section ID which needs a material ID
                    
                    foreach (Material material in materials){
                        // If the IDs list is empty, it means that we want to apply the material to the whole structure (whichi is the default behavior: the default material is set by the constructors of all elements)
                        bool test = false;
                        foreach (string id in material.ids) {
                            if(id != "")
                                test = true;
                        }
                        if (test) {
                            foreach (CrossSection crosec in crossSections) {
                                if(material.ids.Contains((crosec.id - 1).ToString()))
                                    crosec.material = material;
                            }
                        }
                    }
                    status += "Matching with material IDs...\n";
                    
                    foreach (CrossSection crosec in crossSections) {
                        // If the IDs list is empty, it means that we want to apply the cross section to the whole structure (which is the default behavior: the default cross section is set by the constructors of all elements)
                        bool test = false;
                        foreach (string id in crosec.ids) {
                            if (id != "")
                                test = true;
                        }
                        if (test) {
                            foreach (Beam beam in beams) {
                                if (crosec.ids.Contains((beam.id - 1).ToString()))
                                    beam.sec = crosec;
                            }
                        }
                    }
                    status += "Matching with cross section IDs...\n";

                    // Write the data into a .dat file format
                    Parser parser = new Parser(materials, crossSections, nodes, beams, loads);
                    output = parser.file;

                    if (path != "") {
                        status += "Saving file to " + path + "\n";
                        System.IO.File.WriteAllText(@path, output);
                        status += "File saved!\n";
                    }
                }
            }
            catch (Exception e) {
                status += "\nERROR!\n" + e.ToString() + "\n" + e.Data;
            }

            // Return data
            DA.SetData(0, output);
            DA.SetData(1, status);
        }

        // Icon
        protected override System.Drawing.Bitmap Icon {
            get { return Resource.Icon; }
        }

        // Each component must have a unique Guid to identify it. 
        public override Guid ComponentGuid {
            get { return new Guid("{1954a147-f7a2-4d9c-b150-b788821ccae7}"); }
        }
    }
}

Have you stepped into your code with a debug breakpoint to see on what line the exception occurs?

Another question. How are you referencing karamba? Is it a .gha that you renamed as a .dll (since visual studio won’t accept .gha the normal way). I also had the same error as you when I referenced a .gha that I renamed to a .dll for visual studio to reference. AlI the methods worked as .dll but I got that [A]cannot cast to [B] error. I had to force visual studio to reference the .gha via the text editor and then that error went away (for me I was referencing grasshoppers transform.gha)

are you completely sure COFF loading is turned off in GrasshopperDeveloperSettings? In my experience with these kinds of type issues this is the culprit 95% of the time.
image

Which service release of Rhino do you have installed?
I’ve noticed that Karamba isn’t built against say SR0 of Rhino, so maybe it’s a nested rhinocommon type reference that is triggering this error. If you update to the latest SR it might resolve the issue.

I did update references (as newer versions of Karamba have breaking changes from the code as on github), but the open source project did compile, load and work correctly here for me.

As an alternative, I do provide Karamba to Structural analysis components (Sofistik included). You can download from http://www.geometrygym.com/downloads My tools aren’t open source though, and I do charge commercial users to use them (after a free evaluation).

Cheers,

Jon

Hi Michael,

That is exactly what I did! How did you go about forcing visual studio to reference the .gha?
I tried changing the path here: (with notepad++)

Yep, completely sure. This didn’t fix it

Hi Jon,

Thanks, I’m aware of your tools, they’re great. However I would like to make it as easy as possible for other people to use this. Using one component to convert the Karamba-model to a .dat file seems really handy, especially for quick variant studies.

But I guess the problem lies in what Michael said, referencing a renamed .gha instead of the actual .gha.

P.S. I’m in SR14 by the way

That’s what I’m offering, a Karamba to .dat component. Refer to image below.

You can also generate independent model data with my plugin to combine in (such as load case combinations)

I renamed a copy of the .gha back to .dll for referencing in visual studio. You need to ensure that the karamba plugin is loaded prior to yours (which I do by a skeleton dll for the grasshopper component which references a separate dll to do the conversion).

Ah that does look quite good! Is it compatible with Karamba 1.2.2 as well? I downloaded the plugin to have a look, however, it gives the error that it can’t find Karamba 1.14.0.0 (no wonder, cause I don’t have it installed :wink: )

Next to that, I would still like to fix my initial problem :smile:

I’ll email you a direct link (as if I post here it will be broken in the future) but I did update the download link today, http://www.geometrygym.com/downloads ) Putting your code onto a fork would allow myself or others to try and replicate the behaviour you see. You must have made other changes as the github repo you linked is built against old karamba versions that have breaking changes with current. I do also support Karamba 1.3.x

I first completely un-referenced the reference from VS so that I can add the entire reference manually rather than changing the path (path change didn’t work for me). Then I located the .csproj file.
1
Right click that and open in notepad or any editor you prefer.
Scroll down to the section where you see the other references like RhinoCommon and Grasshopper.
The manually add the new reference for your gha.


save the text document and go back to VS and you should get this message about File Modification Detection. Click Reload.

Now you should see it in references and the reference path should be to the gha.