VIKTOR <> Grasshopper, pass geometry back into grasshopper

Hi VIKTORians,

I’m working with a grasshopper file on a app with multiple pages.
And I’m not sure how to import a geometry back into grasshopper.
Could anyone show me how this is done?

— What I’m trying to do—

On the first page on my VIKTOR app I create a simple parametric geometry in grasshopper and this result is saved and I can view it in the 3D viewer.

Now on the next page in my VIKTOR app I want to load this geometry from the first page back into the grasshopper file, in the next part of the grasshopper script. The grasshopper script will then copy the geometry a few times and change the mesh colour. This resulting fresly coloured mesh is then to be exported and viewed in the page 2 geometry viewer. However, its not working since I’m not able to get the geometry back into grasshopper.

---- what I’m actually trying to do ----
I’ve got grasshopper scripts where I’ve split the entire script over different pages.
Lets say the steps from page 1 (VIKTOR) takes 1 minute to calculate in Grasshopper.
And the steps from page 2 takes 2 minutes to calculate in Grasshopper.
When I Click my actionbutton on page 2, I don’t want to have to recalculate step one. So I dont want the calculation time to be 3 minutes at page 2, but I want it to be 2 minutes.

----- what I’m using ----
currently I am using the input.json method from the tutorial to communicate with grasshopper.

input_json = json.dumps(input_data)
        files = [('input.json', BytesIO(bytes(input_json, 'utf8')))]
        generic_analysis = GenericAnalysis(files=files, executable_key="run_grasshopper",
                                           output_filenames=output_filenames)
        generic_analysis.execute(timeout=3600)

I have tried to dump the file, dump the filepath, dump a json-export of the mesh, etc. but no succes.

---- What I’m working with —
Cli v0.33.2
VIKTOR connector v5.21.0
Python 3.9
VIKTOR worker v.5.4.1

Thanks in advance!

1 Like

Hi @Dolf, welcome to our community forum!

Great to see you’ve gotten the first part of your app up and running already, interacting with Grasshopper even!

It reads to me like you are struggling with a fundamental VIKTOR concept here, namely that VIKTOR is stateless. In VIKTOR, results are (by default) not stored. Instead, user input (which can be stored, if you have a simple or tree type app) is in the parametrisation. Then, when visualisations, downloads, calls to other software etc are triggered by the user that input is taken, a result is calculated and shown to the user. The benefit for you as a developer is that the flow is very simple and direct.

However, in some cases (like yours) it’s undesirable, because calculations that take a long time are costly to recalculate multiple times. To accomodate for cases like that we have some options for you to store results. You could for example use the storage to read previously generated results in step 2 (e.g. reading the file from storage, adding it to your files and sending it along to the GenericAnalysis), or use memoization (if possible) to skip the actual calculation and use a previously created result.

Hope this helps get you going in the right direction!?

Regards,
Roeland

Hi Roeland,

Thanks for the quick response, much appreciated :slight_smile:

I was already using storing the results to save and load the 3dm geometry.
However I’m struggling with how to send and load this geometry back to grasshopper.

  1. How do I add the files to the generic_analysis.

Do I add the filename as a string like this:

input_data = {
    'stage2active': "true",
    'xycopies': params.page_2_blue.xycopies,
    'pinkmesh_saved' : "pinkmesh.3dm"

Or do I do I use something like this?

def prepare_input_files(self, params, page_id):
    print("Preparing input files for page;", page_id)
    input_files = []
    if page_id == 'page_2_blue':
        # Example of adding a file from storage
        try:
            pink_mesh_file = Storage().get("pinkmesh.json", scope='entity')
            if pink_mesh_file:
                input_files.append(('pinkmesh.3dm', pink_mesh_file))
        except Exception as e:
            print(f"Error loading file for page_2_blue: {e}")
    # Add conditions for other pages if necessary
    return input_files

with:

input_data = self.prepare_input_data(params, current_page)
input_json = json.dumps(input_data)
files = [('input.json', BytesIO(bytes(input_json, 'utf8')))]
input_files = self.prepare_input_files(params, current_page)
files.extend(input_files)

or using something else?

  1. How do I process this in the run_grasshopper.py that is triggered by the generic_analysis?
    Should I update the input trees?
def convert_inputs_to_datatree(input_params):
    input_trees = []
    for key, value in input_params.items():
        tree = gh.DataTree(key)
        tree.Append([{0}], [str(value)])
        input_trees.append(tree)
    return input_trees

And should any conversion / modifications happen to the 3dm mesh before passing it on?

  1. How should does it work with the hops in grasshopper?

Do I use GetString or does it directly work with GetGeometry?
And what should the name of the hop-input be (e.g as per above: pinkmesh_saved or pinkmesh (which is the mesh name) or pinkmesh.3dm?

Many thanks in advance :blush:

Hi Dolf!

Welcome to the forum! And great to see you building (more complex) Grasshopper apps!

We’ve made multiple Grasshopper apps like these, so I can give you some answers on your questions!

First of all, I would suggest you saving encoded Rhino geometry in our storage. Right now you are:

  • Creating (encoded) Rhino Geometry in Grasshopper
  • Converting the Geometry to a 3dm
  • Saving the 3dm to Storage
  • Send the 3dm to Worker
  • Encoding the 3dm to Rhino Geometry for Grasshopper to use.

We can make it much easier by directly sending the encoded Rhino Geometry to Storage, and when we need it, immediately send it back to Grasshopper. In that case we save the encoded Rhino Geometry in a Json, as well as creating a 3dm for display in step 2.

To give you an idea:

def step1():
    ...
    # Run analysis
    analysis = GenericAnalysis(files=files, executable="run_gh", output_filenames=['pinkmesh.3dm', 'pinkmesh.json'])
    
    # Save the JSON in storage
    storage = Storage()
    pinkmesh = analysis.get_output_file("pinkmesh.json")
    storage.set('pinkmesh.json', data=pinkmesh)
    
    # Show the pinkmesh as result
    return GeometryResult(analysis.get_output_file("pinkmesh.3dm"), geometry_type='3dm')


def step2():
    #  Load step 1 data
    try:
        pink_mesh_file = Storage().get("pinkmesh.json", scope='entity')
    except Exception as e:
        print(f"Error loading file for page_2_blue: {e}")
    
    with pink_mesh_file.open() as fp:
        data_step1 = json.loads(fp.read())
    
    # Prepare data for Generic Analysis step 2
    input_data = {
        'stage2active': "true",
        'xycopies': params.page_2_blue.xycopies,
        'pinkmesh_saved' : data_step1['pinkmesh_encoded']
    }

Doing it this way should also solve question 2, as in you input_data, pinkmesh_saved can be processed by convert_inputs_to_datatree

And it should also solve question 3 :slight_smile: , as there is no conversion needed. For Hops you can use the Get Geometry node, which automatically translates your encoded geometry to needed geometry. In this example make sure your Get Geometry is renamed to 'pinkmesh_saved'.

Some other tips:

  • Try to work with meshes instead of Breps here.
  • When you want more or bigger data to send from step to step; I highly advise to use flattened lists in stead of tree types. You can do it, but it saves some headaches.
  • I see you using a ‘stage2active’ boolean. This led me to suspect you have some switch in Grasshopper and you are using the same Grasshopper file for both steps. You can actually configure your worker yaml to have multiple executables, pointing to different Grasshopper scripts.

Hopefully that helps you out! Let me know if it works and if you come across other issues

Rick

Hi Rick,

Many thanks. Very usefull :smiley:

I think we’re almost there - its not working yet and I suspect it has got to do with the format of the JSON.

So I’m using this:

            try:
                pink_mesh_file = Storage().get("pinkmesh.json", scope='entity')
            except Exception as e:
                print(f"Error loading file for page_2_blue: {e}")

            with pink_mesh_file.open() as fp:
                mesh_data = json.loads(fp.read())
                print("Printing mesh data pour vous:")
                print(mesh_data)

            # Initialize pink_mesh_saved as None or some default value in case 'pinkmesh' is not found
            pink_mesh_saved = None

            # Iterate through each item in the mesh_data list
            for item in mesh_data:
                # Check if this item's 'ParamName' is 'pinkmesh'
                if item['ParamName'] == 'pinkmesh':
                    # If found, extract the part of the mesh data you need, for example, the 'InnerTree'
                    pink_mesh_saved = item['InnerTree']['{0}'][0]
                    break  # Exit the loop once the required data is found


            input_data = {
                'stage2active': "true",
                'xycopies': params.page_2_blue.xycopies,
                'pinkmesh_saved' : pink_mesh_saved
                #'pinkmesh_saved': str(entity_folder_path.parent / "viktor-grasshopper/outputs/pinkmesh.3dm"),
                # Add other parameters specific to page 2 as needed
            }

            if pink_mesh_saved is None:
                print("Error: 'pinkmesh' data not found.")
            else:
                # Proceed with using input_data as needed
                print("Input data prepared successfully.")

and this:

        # Prepare and run the analysis
        print("Output filenames:",output_filenames)
        input_data = self.prepare_input_data(params, current_page)
        print("input data =",input_data)
        input_json = json.dumps(input_data)
        files = [('input.json', BytesIO(bytes(input_json, 'utf8')))]
        #input_files = self.prepare_input_files(params, current_page)
        #files.extend(input_files)
        # print("input files =",input_files)
        print("files =", files)

        #adding the output list:
        mesh_files = [filename.replace('.3dm', '') for filename in output_filenames if filename.endswith('.3dm')]
        data_files = [filename.replace('.json', '') for filename in output_filenames if filename.endswith('.json')]

        output_specs = [{"mesh": mesh, "data": data_files} for mesh in mesh_files]
        outputs_json = json.dumps(output_specs)
        files.append(('outputs.json', BytesIO(bytes(outputs_json, 'utf8'))))
        print("files =", files)
        print("Generated outputs.json:", outputs_json)

        #run the analysis using the files and output_filenames
        generic_analysis = GenericAnalysis(files=files, executable_key="run_grasshopper",
                                           output_filenames=output_filenames)
        generic_analysis.execute(timeout=3600)

In my little log from run_grasshopper I can find this:

2024-02-15 15:52:15,761 - INFO - Input parameters:{
“stage2active”: “true”,
“xycopies”: 2,
“pinkmesh_saved”: {
“type”: “Rhino.Geometry.Mesh”,
“data”: “{"version":10000,"archive3dm":60,"opennurbs":-1903654895,"data":"+n8CAGcDAAAAAAAA+/8CABQAAAAAAAAA5NTXTkfp0xG/5QAQgwEi8C25G1z8/wIALwMAAAAAAAA4GAAAAAYAAADSHTOevfjl/9IdM569+OX/0h0znr345f/SHTOevfjl/9IdM569+OX/0h0znr345f/SHTOevfjl/9IdM569+OX/AAAAAAAAAAAAAAAAAAAAAAAAgD8AAIA/AACAPwAAgL8AAIC/AACAvwAAgD8AAIA/AACAPwAAgL8AAIC/AACAvwAAgD8AAIA/AACAvwAAgL//////AAAAAAABAAAAAAECAwQFBgcICQoLDA0ODxAREhMUFRYXIAEAAFJfoUQBAIAAQD4AAAAAAAAAeNpjYEAHDg4QusEBlY1NDTYAk3NwwFSHbE6DA6ZZIDa6GnT3IKuBsdHNQGYjq8FlFzY1EDYApqcVAWpXQpUgAQAAPE+V9wEAgABAIwAAAAAAAAB42mNggIGG/QxEsYlVhwxI0dNgjyRGBJu2egAYmRfpsrvadAAAAAAAAAAAYAAAACqaPecA/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAQKQAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwPwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8D8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPA/AAAAAGk8mKMAAAAAAQCAAEBoAAAAAAAAAAEAAAAAAAAAGAAAAEACAAAV8y/GAQCAAEBDAAAAAAAAAHjaY2AgBnA4oPIFHPCLE2sOsQBdH4cDcebhco+AA3HugokTModQ+OAyB12ckDtwieMyh1R/EWsOQhwAc0cKgYABjATg1whJ0h0znr345f/SHTOevfjl/9IdM569+OX/0h0znr345f/SHTOevfjl/9IdM569+OX/USb4l/9/AoAAAAAAAAAAAA=="}”
}
}
2024-02-15 15:52:15,811 - INFO - Error during Grasshopper definition evaluation: Expecting value: line 1 column 1 (char 0)

and this results in the follow print from app.py

Printing mesh data pour vous:
[{‘ParamName’: ‘pinkmesh’, ‘InnerTree’: {‘{0}’: [{‘type’: ‘Rhino.Geometry.Mesh’, ‘data’: ‘{“version”:10000,“archive3dm”:60,“opennurbs”:-1903654895,“data”:“+n8CAGcDAAAAAAAA+/8CABQAAAAAAAAA5NTXTkfp0xG/5QAQgwEi8C25G1z8/wIALwMAAAAAAAA4G
AAAAAYAAADSHTOevfjl/9IdM569+OX/0h0znr345f/SHTOevfjl/9IdM569+OX/0h0znr345f/SHTOevfjl/9IdM569+OX/AAAAAAAAAAAAAAAAAAAAAAAAgD8AAIA/AACAPwAAgL8AAIC/AACAvwAAgD8AAIA/AACAPwAAgL8AAIC/AACAvwAAgD8AAIA/AACAvwAAgL//////AAAAAAABAAAAAAECAwQFBgcIC
QoLDA0ODxAREhMUFRYXIAEAAFJfoUQBAIAAQD4AAAAAAAAAeNpjYEAHDg4QusEBlY1NDTYAk3NwwFSHbE6DA6ZZIDa6GnT3IKuBsdHNQGYjq8FlFzY1EDYApqcVAWpXQpUgAQAAPE+V9wEAgABAIwAAAAAAAAB42mNggIGG/QxEsYlVhwxI0dNgjyRGBJu2egAYmRfpsrvadAAAAAAAAAAAYAAAACqaPecA/wD/A
P8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAQKQAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPA/AAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwPwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8D8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPA/AAAAAGk8mKMAAAAAAQCAAEBoAAAAAAAAAAEAAAAAAAAAGAAAAEACAAAV8y/GAQCAAEBDAAAAAAAAAHjaY2AgBnA4oPIFHPCLE2sOsQBdH4cDcebhco+AA3HugokTModQ+OAyB12ckDtwieMyh1R/EWsOQhwAc0cKgYABjATg1whJ0h0znr345f/SHTOevfjl/9IdM569+OX/0h0znr345f/SHTOevfjl/9IdM569+OX/USb4l/9/AoAAAAAAAAAAAA==”}’}]}}]
Input data prepared successfully.
input data = {‘stage2active’: ‘true’, ‘xycopies’: 2, ‘pinkmesh_saved’: {‘type’: ‘Rhino.Geometry.Mesh’, ‘data’: ‘{“version”:10000,“archive3dm”:60,“opennurbs”:-1903654895,“data”:“+n8CAGcDAAAAAAAA+/8CABQAAAAAAAAA5NTXTkfp0xG/5QAQgwEi8C2
5G1z8/wIALwMAAAAAAAA4GAAAAAYAAADSHTOevfjl/9IdM569+OX/0h0znr345f/SHTOevfjl/9IdM569+OX/0h0znr345f/SHTOevfjl/9IdM569+OX/AAAAAAAAAAAAAAAAAAAAAAAAgD8AAIA/AACAPwAAgL8AAIC/AACAvwAAgD8AAIA/AACAPwAAgL8AAIC/AACAvwAAgD8AAIA/AACAvwAAgL//////AAA
AAAABAAAAAAECAwQFBgcICQoLDA0ODxAREhMUFRYXIAEAAFJfoUQBAIAAQD4AAAAAAAAAeNpjYEAHDg4QusEBlY1NDTYAk3NwwFSHbE6DA6ZZIDa6GnT3IKuBsdHNQGYjq8FlFzY1EDYApqcVAWpXQpUgAQAAPE+V9wEAgABAIwAAAAAAAAB42mNggIGG/QxEsYlVhwxI0dNgjyRGBJu2egAYmRfpsrvadAAAAAA
AAAAAYAAAACqaPecA/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAQKQAAAAAAAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAPA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwPwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8D8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPA/AAAAAGk8mKMAAAAAAQCAAEBoAAAAAAAAAAEAAAAAAAAAGAAAAEACAAAV8y/GAQCAAEBDAAAAAAAAAHjaY2AgBnA4oPIFHPCLE2sOsQBdH4cDcebhco+AA3HugokTModQ+OAyB12ckDtwieMyh1R/EWsOQhwAc0cKgYABjATg1whJ0h0znr345f/SHTOevfjl/9IdM569+OX/0h0znr345f/SHTOevfjl/9IdM569+OX/USb4l/9/AoAAAAAAAAAAAA==”}’}}
files = [(‘input.json’, <_io.BytesIO object at 0x0000014A306FDB30>)]
files = [(‘input.json’, <_io.BytesIO object at 0x0000014A306FDB30>), (‘outputs.json’, <_io.BytesIO object at 0x0000014A3071C9A0>)]
Generated outputs.json: [{“mesh”: “bluemesh”, “data”: [“xyCopies”]}]
2024-02-15 15:52:14.911 INFO : External analysis not yet finished, waiting 1 seconds to retry…
2024-02-15 15:52:16.077 ERROR : Exception is raised
Traceback (most recent call last):
File “viktor_connector\connector.pyx”, line 295, in connector.Job.execute
File “viktor\core.pyx”, line 1924, in viktor.core._handle_job
File “viktor\core.pyx”, line 1870, in viktor.core._handle_job._handle_button
File “C:\Users\dolfb\PycharmProjects\DolfLab\VIKTORApp\BabySteps\app.py”, line 286, in set_page_2_and_run_grasshopper
self.run_grasshopper_script(params, **kwargs)
File “C:\Users\dolfb\PycharmProjects\DolfLab\VIKTORApp\BabySteps\app.py”, line 107, in run_grasshopper_script
generic_analysis.execute(timeout=3600)
File “viktor\external\external_program.pyx”, line 385, in viktor.external.external_program.ExternalProgram.execute
viktor.errors.ExecutionError: Something went wrong during the execution.

When I see your script I suspect it has got to do with how you get from the pinkmesh to a pinkmesh_encoded?

Could you please assist and have a look at the info above?
I’ve been trying to find info on the json-format it should have but I couldn’t find anything.

Many thanks in advance :slightly_smiling_face:

Hi Dolf; Nice to see you making progress.

I have looked through it and I think updating this is a very good first step:

# Change 
 'pinkmesh_saved' : pink_mesh_saved
# To
 'pinkmesh_saved' : json.dumps(pink_mesh_saved)

Hi Rick,

Thanks for the response unfortunately its not helping.
I’ve been trying many variations in adjusting the json and passing the data but no succes.

Not sure where to look now.

As a way forward:

  1. I have attached the JSON file > Maybe you see something thats off
    (I’m not sure is there a method to try and test it in grasshopper?? I tried {file path}>{read file}> {GetGeometry} but that doesnt work.)

pinkmesh.json (1.5 KB)

  1. not sure if its of any help but this is what the relevant section of code looks like:
            try:
                pink_mesh_file = Storage().get("pinkmesh.json", scope='entity')
                with pink_mesh_file.open() as fp:
                    data_step1 = json.loads(fp.read())

                print("Printing mesh data pour vous:")
                print(data_step1)
            except Exception as e:
                print(f"Error loading file for page_2_blue: {e}")
                data_step1 = "{}"  # Setting fallback here for clarity



            input_data = {
                'stage2active': "true",
                'xycopies': params.page_2_blue.xycopies,
                'pinkmesh': json.dumps(data_step1),

And this is what the grasshopper log shows:

2024-02-15 19:03:50,062 - INFO - Input parameters:{
“stage2active”: “true”,
“xycopies”: 7,
“pinkmesh”: “[{"ParamName": "pinkmesh", "InnerTree": {"{0}": [{"type": "Rhino.Geometry.Mesh", "data": "{\"version\":10000,\"archive3dm\":60,\"opennurbs\":-1903654895,\"data\":\"+n8CAGUDAAAAAAAA+/8CABQAAAAAAAAA5NTXTkfp0xG/5QAQgwEi8C25G1z8/wIALQMAAAAAAAA4GAAAAAYAAADSHTOevfjl/9IdM569+OX/0h0znr345f/SHTOevfjl/9IdM569+OX/0h0znr345f/SHTOevfjl/9IdM569+OX/AAAAAAAAAAAAAAAAAAAAAAAAgD8AAIA/AACAPwAAgL8AAIC/AACAvwAAgD8AAIA/AACAPwAAgL8AAIC/AACAvwAAgD8AAIA/AACAvwAAgL//////AAAAAAABAAAAAAECAwQFBgcICQoLDA0ODxAREhMUFRYXIAEAABsxkdgBAIAAQD0AAAAAAAAAeNpjYEAHDfZQ2gGVjU0NNgDXY4+pDtkcGBtZDVgPmhp09yCrgbHRzUAxzx5VLza7sKmBsAEqtBrpK4UtHyABAAA8T5X3AQCAAEAjAAAAAAAAAHjaY2CAgYb9DESxiVWHDEjR02CPJEYEm7Z6ABiZF+myu9p0AAAAAAAAAABgAAAAKpo95wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgABApAAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8D8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwPwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8D8AAAAAaTyYowAAAAABAIAAQGcAAAAAAAAAAQAAAAAAAAAYAAAAQAIAAGXJc24BAIAAQEIAAAAAAAAAeNpjYCAGfLBH5Qs44Bcn1hxiAbo+GJ+Qebjcgy6OyxyYOCFzCIUPLnPQxQm5A5c4LnNI9Rex5iDEAbotICl1LHzt3840LNIdM569+OX/0h0znr345f/SHTOevfjl/9IdM569+OX/0h0znr345f/SHTOevfjl/43cKXH/fwKAAAAAAAAAAAA=\"}"}]}}]”
}
2024-02-15 19:03:50,102 - INFO - Error during Grasshopper definition evaluation: Expecting value: line 1 column 1 (char 0)

Last detail, that might be worth sharing: I’m using Rhino7.

Any thoughts?

tnx :slightly_smiling_face:

Okay, I’ll zoom out a bit.

This snippet works:

# Create input tree list
input_trees = []
tree = gh.DataTree('parameter_name')
tree.Append([{0}], [
    "{\"version\":10000,\"archive3dm\":60,\"opennurbs\":-1903654895,\"data\":\"+n8CAGUDAAAAAAAA+/8CABQAAAAAAAAA5NTXTkfp0xG/5QAQgwEi8C25G1z8/wIALQMAAAAAAAA4GAAAAAYAAADSHTOevfjl/9IdM569+OX/0h0znr345f/SHTOevfjl/9IdM569+OX/0h0znr345f/SHTOevfjl/9IdM569+OX/AAAAAAAAAAAAAAAAAAAAAAAAgD8AAIA/AACAPwAAgL8AAIC/AACAvwAAgD8AAIA/AACAPwAAgL8AAIC/AACAvwAAgD8AAIA/AACAvwAAgL//////AAAAAAABAAAAAAECAwQFBgcICQoLDA0ODxAREhMUFRYXIAEAABsxkdgBAIAAQD0AAAAAAAAAeNpjYEAHDfZQ2gGVjU0NNgDXY4+pDtkcGBtZDVgPmhp09yCrgbHRzUAxzx5VLza7sKmBsAEqtBrpK4UtHyABAAA8T5X3AQCAAEAjAAAAAAAAAHjaY2CAgYb9DESxiVWHDEjR02CPJEYEm7Z6ABiZF+myu9p0AAAAAAAAAABgAAAAKpo95wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgABApAAAAAAAAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8D8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwPwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8D8AAAAAaTyYowAAAAABAIAAQGcAAAAAAAAAAQAAAAAAAAAYAAAAQAIAAGXJc24BAIAAQEIAAAAAAAAAeNpjYCAGfLBH5Qs44Bcn1hxiAbo+GJ+Qebjcgy6OyxyYOCFzCIUPLnPQxQm5A5c4LnNI9Rex5iDEAbotICl1LHzt3840LNIdM569+OX/0h0znr345f/SHTOevfjl/9IdM569+OX/0h0znr345f/SHTOevfjl/43cKXH/fwKAAAAAAAAAAAA=\"}"
])
input_trees.append(tree)

output = gh.EvaluateDefinition(workdir + "test.gh", input_trees)

You’ll see that the data that we need is only the “data” from your JSON.

In that case this might fix it for now:

data_step1 = json.loads(fp.read())[0]['InnerTree']['{0}'][0]['data']

However, this is clearly not a preferred or scalable solution. I’ll give you some snippets on this:

... # Step 1 output prep
output = gh.EvaluateDefinition(workdir + "test1.gh", input_trees)

def get_value_from_tree(datatree: dict, param_name: str):
    """Get first value in datatree that matches given param_name"""
    for df_val in datatree["values"]:
        if df_val["ParamName"] == param_name:
            return df_val["InnerTree"]
    return None

OUTPUTS = ["output1", "output2", "output3"]

for key in OUTPUTS:
    val = get_value_from_tree(output, key)
    output_values[key] = val

# Save json file with output data to working directory
with open(workdir + "pinkmesh.json", "w") as f:
    json.dump(output_values, f)

# Also create the 3dm


... # ON THE VIKTOR APP
def step1():
    ...
    # Run analysis
    analysis = GenericAnalysis(files=files, executable="run_gh", output_filenames=['pinkmesh.3dm', 'pinkmesh.json'])
    
    # Save the JSON in storage
    storage = Storage()
    pinkmesh = analysis.get_output_file("pinkmesh.json", as_file=True)
    storage.set('pinkmesh.json', data=pinkmesh)
    
    # Show the pinkmesh as result
    return GeometryResult(analysis.get_output_file("pinkmesh.3dm"), geometry_type='3dm')

def step2():
    #  Load step 1 data
    try:
        pink_mesh_file = Storage().get("pinkmesh.json", scope='entity')
    except Exception as e:
        print(f"Error loading file for page_2_blue: {e}")
    
    with pink_mesh_file.open() as fp:
        data_step1 = json.loads(fp.read())
    
    # Prepare data for Generic Analysis step 2
    input_data = {
        'stage2active': "true",
        'xycopies': params.page_2_blue.xycopies,
        'pinkmesh_saved' : data_step1['output1']
    }


... # On the worker for Step 2

# Read input parameters from JSON file
with open(workdir + "input.json") as f:
    input_params = json.load(f)

# Initialize the inputs that aren't optimized
input_trees = []
for key, value in input_params.items():
    tree = gh.DataTree(key)
    if isinstance(value, list):
        tree.Append([{0}], value)
    else:
        tree.Append([{0}], [str(value)])
    input_trees.append(tree)

# Evaluate the Grasshopper definition
output = gh.EvaluateDefinition(workdir + "step2.gh", input_trees)

I have checked some projects and as far as I could find this is how I have set it up. Hopefully the first (ugly) approach helped you out, and otherwise this snippet gives a bit of an insight on this process.

If there are still issues, we could also discuss over a online meeting!

Hi Rick,

Many thanks! I really appreciate it.
Unfortunately I’m travelling now + tomorrow. So I can only test it on Saturday.
I’ll let you know how it goes! :slight_smile:

Have a great weekend!

Hi Rick!

Its working :slight_smile:
Many thanks for your assistance!

The problem was cause 1) no referring all the way down to the data section of the mesh json.
2) (actually not happening in my data previous shared data) → but I was double serializing the json. It was also json dumping elsewhere in my code. This resulted in a whole lot of extra “”\\

But its all fixed!
So now I’ve got my base template working :partying_face: :smiley:

Upwards and onwards! :rocket: :ringer_planet:

3 Likes

Nice to hear!

Good luck with your projects!