S3D Integration

You can automatically integrate your calculation pack with S3D, by writing a s3d_integrations.js file. The purpose of this file is to extract data out of your model and analysis results, to run your calculations off. If your calc pack has a s3d_integration.js file and your calculate.js follows the standardised format, your solution will appear in the list in S3D:

From here, your model will be auto-integrated with the following UI:



This file uses the UI format for S3D! So for instance, use s3d_model.elements not s3d_model.members!

Use this file to compile an array of inputs for your calculator, that can be then run in batches:

module.exports = function(s3d_model, analysis_results) {
//you will be given the model and analysis results to work from
if (!analysis_results) throw new Error("No Analysis Results. Please solve model first."); //if this is required, you can throw an error like this
// Perform Logic, like looping through members in your s3d_model and building your input JSONs
//return a list of inputs, these will get looped through the calculate.js file
return [
{a:100, b:200},
{a:50, b:80},
{a:60, b:150},
{a:22, b:30.33}

Helper Functions#


Returns an array of design forces for each member. It will scan all load combos and determine the worst result for each member in both + and - directions.



null, //element 0
{ //element 1
"Mx_pos": 0.00021104637,
"Mx_neg": -0.00013586061,
"My_pos": 0.0080873322,
"My_neg": -0.01786888,
"Mz_pos": 0.080279695,
"Mz_neg": -0.15996633,
"Vx_pos": 0.58564752,
"Vx_neg": -0.53670695,
"Vy_pos": 3.13638791,
"Vy_neg": -3.14150903,
"Vz_pos": 0.3406327,
"Vz_neg": -0.13170178,
"dx": 0.0000025815527,
"dy": -0.00041438211,
"dz": 0,
"Mx_abs": 0.00021104637, //absolute value of Mx_pos, Mx_neg
"My_abs": 0.01786888,
"Mz_abs": 0.15996633,
"Vx_abs": 0.58564752,
"Vy_abs": 3.14150903,
"Vz_abs": 0.3406327


Returns the member's length, including if offsets are used

StructureHelpers.getMemberLength(s3d_model, elem_id, consider_offsets)


Convert units from one system to another. This is very useful when trying to control the inputs coming into your calculation.js file. For instance, say someone is using a model that is in ft, but your calculator only works for mm, you can use UnitHelpers.convert( member_length , s3d_units.length, "mm"); to take it from whatever system the s3d_model is and translate that input to mm

UnitHelpers.convert( value , unit_from, unit_to);
UnitHelpers.convert( 1223 ,"ft", "in");
UnitHelpers.convert( 1223 , s3d_units.length, "mm"); //convert the S3D model unit to a fixed input unit


Below is an example of a more advanced integration for aluminimum design. It takes in the s3d_model and results, in order to compile an array of inputs. It imports the member's length, section properties and design forces.

module.exports = function(s3d_model, analysis_results) {
if (!analysis_results) throw new Error("No Analysis Results. Please solve model first.");
let s3d_units = s3d_model.settings.units;
let materials = s3d_model.materials;
let default_input = {"L":0,"b":null,"d":null,"t1":2.5,"t2":4.5,"J":0,"welded":"No","alloy":"6005","temper":"T5","Mz":1800000,"My":500000,"Vz":20000,"Vy":20000,"Nc":0,"Nt":20000}
let all_design_forces = StructureHelpers.getDesignForces(analysis_results);
let design_members = [];
var members = s3d_model.elements;
for (var i =0; i < members.length; i++) {
var this_member = members[i];
if (!this_member) continue;
let this_member_input = JSON.parse(JSON.stringify(default_input)); //start with this
let span_L = StructureHelpers.getMemberLength(s3d_model, i);
let design_forces = all_design_forces[i];
this_member_input.L = UnitHelpers.convert(span_L, s3d_units.length, "mm");
this_member_input.Mz = UnitHelpers.convert(design_forces.Mz_abs, s3d_units.moment, "N-mm");
this_member_input.My = UnitHelpers.convert(design_forces.My_abs, s3d_units.moment, "N-mm");
this_member_input.Vz = UnitHelpers.convert(design_forces.Vz_abs, s3d_units.force, "N");
this_member_input.Vy = UnitHelpers.convert(design_forces.Vy_abs, s3d_units.force, "N");
this_member_input.Nc = UnitHelpers.convert(design_forces.Vx_pos, s3d_units.force, "N");
this_member_input.Nt = UnitHelpers.convert(design_forces.Vx_neg, s3d_units.force, "N");
//sections data
let section_id = this_member[2];
let section_obj = s3d_model.sections[section_id];
let material_id = section_obj.material_id;
this_member_input.J = UnitHelpers.convert(section_obj.J, s3d_units.section_length+"^4", "mm^4");
let shape = section_obj.aux.polygons[0].shape;
let dims = section_obj.aux.polygons[0].dimensions;
this_member_input.t1 = "";
this_member_input.t2 = "";
if (shape == "hollow rectangle") {
this_member_input.shape = "hollow rectangular";
this_member_input.s3d_section = section_id;
this_member_input.legs = 2;
this_member_input.b = UnitHelpers.convert(dims.b.value, s3d_units.section_length, "mm");
this_member_input.d = UnitHelpers.convert(dims.h.value, s3d_units.section_length, "mm");
this_member_input.t1 = UnitHelpers.convert(dims.t.value, s3d_units.section_length, "mm");
this_member_input.t2 = UnitHelpers.convert(dims.tb.value, s3d_units.section_length, "mm");
} else {
this_member_input.shape = "custom";
this_member_input.s3d_section = section_id;
if (section_obj.aux.depth) this_member_input.d = UnitHelpers.convert(section_obj.aux.depth, s3d_units.section_length, "mm");
if (section_obj.aux.width) this_member_input.b = UnitHelpers.convert(section_obj.aux.width, s3d_units.section_length, "mm");
if (section_obj.aux.polygons[0] && section_obj.aux.polygons[0].alum) {
var alum_obj = section_obj.aux.polygons[0].alum;
if (alum_obj.hasOwnProperty("number_of_legs")) this_member_input.legs = alum_obj.number_of_legs;
if (alum_obj.hasOwnProperty("leg_thickness")) this_member_input.t1 = UnitHelpers.convert(alum_obj.leg_thickness, s3d_units.section_length, "mm");
if (alum_obj.hasOwnProperty("leg_height")) this_member_input.d = UnitHelpers.convert(alum_obj.leg_height, s3d_units.section_length, "mm");
if (!this_member_input.t2 && this_member_input.t1) this_member_input.t2 = this_member_input.t1; //just make them the same if blank
//clean and round some data
this_member_input.L = parseFloat(this_member_input.L.toFixed(0))
this_member_input.J = parseFloat(this_member_input.J.toFixed(0))
this_member_input.Mz = parseFloat(this_member_input.Mz.toFixed(0))
this_member_input.My = parseFloat(this_member_input.My.toFixed(0))
this_member_input.Vy = parseFloat(this_member_input.Vy.toFixed(0))
this_member_input.Vz = parseFloat(this_member_input.Vz.toFixed(0))
this_member_input.Nc = parseFloat(this_member_input.Nc.toFixed(0))
this_member_input.Nt = parseFloat(this_member_input.Nt.toFixed(0))
if (this_member_input.h) this_member_input.h = parseFloat(this_member_input.h.toFixed(0))
if (this_member_input.b) this_member_input.b = parseFloat(this_member_input.b.toFixed(0))
design_members[i] = this_member_input; //add to array
if (design_members.length == 0) { //always a good idea - tell the user why this could be the case
throw new Error("No Members Imported. Check your materials are set to 'aluminum' and that the sections are hollow rectangular");
return design_members;

How to Test my Code#

To test your code, first start by creating a Draft. Then take the UID of that draft and run the following code in your S3D console:


This will open up the module on the left panel and run your latest s3d_integration.js file on the server.