using FeMM.Common.Models;
using FeMM.Grasshopper.DataTypes.FeMM;
using FeMM.Grasshopper.Helpers;
using Grasshopper.Kernel;
using Grasshopper.Kernel.Data;
using Grasshopper.Kernel.Special;
using Grasshopper.Kernel.Types;
using MeChecksApi;
using Rhino.Geometry;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Versioning;
namespace FeMM.Grasshopper.Components.MeCheck
{
#if NETCOREAPP
[SupportedOSPlatform("windows")]
#endif
public class SuperElementCheckComponent : GH_Component
{
protected bool _run;
protected List<string> _casesNames;
public List<string> CasesNames => _casesNames;
public bool IsRunning { get; set; }
/// <summary>
/// Initializes a new instance of the ChecksComponent class.
/// </summary>
public SuperElementCheckComponent()
: base("Super Element Check", "SEC", "Perform the check of a Super-Element through the external MeCheck software",
CategoryNameConstants.CATEGORY_CHECKS, CategoryNameConstants.SUBCATEGORY_MECHECK)
{
_run = false;
_casesNames = [];
}
public override void CreateAttributes()
{
var attr = new ComponentAttributes.ComponentOneButtonAttributes(this, "Run");
attr.ButtonPressed += () =>
{
_run = true;
ExpireSolution(true);
};
m_attributes = attr;
}
/// <summary>
/// Registers all the input parameters for this component.
/// </summary>
protected override void RegisterInputParams(GH_InputParamManager pManager)
{
pManager.AddGenericParameter("Models", "M", "The FEM models (more than one for stages)", GH_ParamAccess.list);
pManager.AddTextParameter("Super-Element", "SE", "The name of the Super-Element to filter", GH_ParamAccess.item);
pManager[1].Optional = true;
pManager.AddGenericParameter("Load Combinations", "LCB", "The load cases combinations", GH_ParamAccess.tree);
pManager[2].Optional = true;
pManager.AddTextParameter("Output Path", "O", "The full path of the file MeCheck to create", GH_ParamAccess.item);
}
/// <summary>
/// Registers all the output parameters for this component.
/// </summary>
protected override void RegisterOutputParams(GH_OutputParamManager pManager)
{
pManager.AddGenericParameter("Model", "M", "The FEM model", GH_ParamAccess.item);
}
/// <summary>
/// This is the method that actually does the work.
/// </summary>
/// <param name="DA">The DA object is used to retrieve from inputs and store in outputs.</param>
protected override void SolveInstance(IGH_DataAccess DA)
{
Message = "";
var models = new List<GH_Model>();
string super_element = "";
string outputPath = "";
if (!DA.GetDataList("Models", models))
return;
if (!DA.GetData("Output Path", ref outputPath))
return;
if (Path.GetExtension(outputPath).ToLower() != ".mec")
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Invalid extension in file name");
return;
}
// Retrieve all te super elements used in the model
Common.Helpers.CommonModelHelper.GetSuperElements(models[0].Value, out List<SuperElementAttributeModel> superElements);
// Connect the ValueList component on the analysis results input item
IEnumerable<IGH_Param> keys_analysisResultLists = Params.Input[1].Sources.
Where(s => s.GetType() == typeof(GH_ValueList));
foreach (GH_ValueList vallist in keys_analysisResultLists)
{
if (vallist.ListMode != GH_ValueListMode.DropDown)
vallist.ListMode = GH_ValueListMode.DropDown;
List<string> names = [.. superElements.Select(se => se.Value).Distinct()];
ComponentsHelper.SetValueList(vallist, names);
vallist.NickName = "Super-Element";
}
if (DA.GetData(1, ref super_element))
{
if (superElements.Count(a => a.Value == super_element) == 0)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"The super element {super_element} does not exists.");
return;
}
}
else
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Input parameter Super-Element failed to collect data");
}
bool has_combinations = DA.GetDataTree(2, out GH_Structure<IGH_Goo> combinations);
_casesNames.Clear();
int m = 1;
for (int i = 0; i < models.Count; i++)
{
GH_Model model = models[i];
// Add the model load and combination cases from results
var modelLoadCases = new List<CaseModel>();
modelLoadCases.AddRange(model.Value.Elements.SelectMany(e => e.Results).Select(l => l.LoadCase).Distinct());
foreach (CaseModel loadCase in modelLoadCases)
{
string lcase_name = $"{m}-{loadCase.Name}";
_casesNames.Add(lcase_name);
}
m++;
}
if (_run)
{
// Check the super beam
List<BeamModel> super_beam = [.. models[0].Value.Beams.Where(b => b.HasAttribute(typeof(SuperElementAttributeModel)) &&
SuperElementAttributeModel.GetSuperElement(b) == super_element)];
// First find the endnodes
double tol = ModelHelper.GetTolerance();
var endbeams = new List<string>();
for (int i = 0; i < super_beam.Count; i++)
{
BeamModel beam = super_beam[i];
int n = super_beam.Count(b => b.BeamId != beam.BeamId && (b.PointFrom.DistanceTo(beam.PointFrom) < tol ||
b.PointFrom.DistanceTo(beam.PointTo) < tol || b.PointTo.DistanceTo(beam.PointFrom) < tol ||
b.PointTo.DistanceTo(beam.PointTo) < tol));
if (n == 1)
endbeams.Add(beam.BeamId);
}
// No breaks alloewd
if (endbeams.Count != 2)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "The super-beam must be continuous and without breaks.");
return;
}
int lu, fu;
switch (models[0].Value.Units)
{
case ModelModel.ModelUnits.kNm:
lu = MeChecksConsts.LenghtUnit_Metre;
fu = MeChecksConsts.ForceUnit_KNewton;
break;
case ModelModel.ModelUnits.SI:
lu = MeChecksConsts.LenghtUnit_Metre;
fu = MeChecksConsts.ForceUnit_Newton;
break;
case ModelModel.ModelUnits.Nmm:
lu = MeChecksConsts.LenghtUnit_Millimetre;
fu = MeChecksConsts.ForceUnit_Newton;
break;
default:
return;
}
IMeChecksApi api = ApiHelper.Connect();
if (api == null)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Unable to connect with meChecks. Please check in the program is running");
return;
}
if (api.NewJob(models[0].Value.Name, lu, fu, MeChecksConsts.TempUnit_Celsius) != MeChecksConsts.Error_Success)
throw new Exception("Failed to create new model");
// Add geometri from the first model
foreach (NodeModel node in models[0].Value.Nodes)
{
api.AddJoint(node.NodeId, node.Position.X, node.Position.Y, node.Position.Z);
}
foreach (BeamModel beam in models[0].Value.Beams)
{
IEnumerable<NodeModel> fromNodes = models[0].Value.Nodes.Where(n => n.Position.DistanceTo(beam.PointFrom) < tol);
IEnumerable<NodeModel> toNodes = models[0].Value.Nodes.Where(n => n.Position.DistanceTo(beam.PointTo) < tol);
NodeModel node1 = fromNodes.First();
NodeModel node2 = toNodes.First();
if (node1 == null || node2 == null)
{
throw new Exception(string.Format("Unreconized node(s) at coordinates {0} and/or {1}", beam.PointFrom, beam.PointTo));
}
api.AddFrame(beam.BeamId, node1.NodeId, node2.NodeId, beam.PointFrom.DistanceTo(beam.PointTo));
}
// Add load cases and the tensions for the models
m = 1;
//_casesNames.Clear();
foreach (GH_Model model in models)
{
// Check if model not exists
if (api.IsModelExisting(model.Value.Name) == MeChecksConsts.False)
api.AddModel(model.Value.Name);
// Add the model load and combination cases
/*ModelHelper.GetLoadCases(model.Value, out List<LoadCaseModel> modelLoadCases);
foreach (LoadCaseModel loadCase in modelLoadCases)
{
string lcase_name = $"{m}-{loadCase.Name}";
api.AddModelLoadCase(model.Value.Name, lcase_name);
api.AddLoadCaseToComboTable(lcase_name);
//_casesNames.Add(lcase_name);
}
foreach (LoadCombinationModel combo in model.Value.LoadCombinations)
{
string combo_name = $"{m}-{combo.Name}";
api.AddModelLoadCase(model.Value.Name, combo_name);
api.AddLoadCaseToComboTable(combo_name);
//_casesNames.Add(combo_name);
}*/
foreach (string case_name in _casesNames.Where(n => n.StartsWith($"{m}-")))
{
api.AddModelLoadCase(model.Value.Name, case_name);
api.AddLoadCaseToComboTable(case_name);
}
// Add the frame forces
foreach (BeamModel beam in model.Value.Beams)
{
Guid guid = model.Guids[beam];
BeamModel ref_beam = (BeamModel)models[0].Guids.First(g => g.Value == guid).Key;
foreach (BeamForceResultModel force in beam.Results)
{
api.AddFrameForce(model.Value.Name, ref_beam.BeamId, force.Station, $"{m}-{force.LoadCase.Name}", "",
force.AxialForce, -force.ShearForce.Y, force.ShearForce.X, force.Torque, -force.BendingMoment.X,
force.BendingMoment.Y);
}
}
m++;
}
// Find the Starting beam
double min_x = Math.Min(super_beam.Min(b => b.PointFrom.X), super_beam.Min(b => b.PointTo.X));
double min_y = Math.Min(super_beam.Min(b => b.PointFrom.Y), super_beam.Min(b => b.PointTo.Y));
var min_pt = new Point3d(min_x, min_y, 0);
BeamModel starting_beam = endbeams.Select(id => models[0].Value.GetBeam(id))
.OrderBy(b => Math.Min(min_pt.DistanceTo(b.PointFrom), min_pt.DistanceTo(b.PointTo))).First();
// Finally set the Frames To Check
api.AddFrameToCheck(starting_beam.BeamId);
BeamModel last_beam = starting_beam;
for (int n = 1; n < super_beam.Count; n++)
{
api.GetFramesToCheck(out string[] framesToCheck);
BeamModel beam = super_beam.Single(b => !framesToCheck.Contains(b.BeamId) && (
last_beam.PointFrom.DistanceToSquared(b.PointFrom) < tol ||
last_beam.PointFrom.DistanceToSquared(b.PointTo) < tol ||
last_beam.PointTo.DistanceToSquared(b.PointFrom) < tol ||
last_beam.PointTo.DistanceToSquared(b.PointTo) < tol));
api.AddFrameToCheck(beam.BeamId);
last_beam = beam;
}
// Set the load combinations
var load_combinations = new List<LoadCaseCombinationModel>();
if (has_combinations)
{
var cnames = new List<string>();
for (int i = 0; i < combinations.Branches.Count; i++)
{
List<IGH_Goo> row = (List<IGH_Goo>)combinations.get_Branch(i);
if (i == 0) // First rows: check cases name
{
cnames.AddRange(row.Cast<GH_String>().Where(c => c.Value != "").Select(c => c.Value));
IEnumerable<string> invalid_names = cnames.Where(c => !_casesNames.Contains(c));
if (invalid_names.Count() > 0)
{
AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "Invalid cases name: " + string.Join(", ", invalid_names));
break;
}
}
else
{
string name = ((GH_String)row[0]).Value;
IEnumerable<double> incrms = row.Cast<GH_String>()
.Where(c => double.TryParse(c.Value, out double d))
.Select(c => Convert.ToDouble(c.Value));
api.AddLoadCombination(name, [.. cnames], [.. incrms]);
}
}
}
api.UpdateUI();
api.BringToTop();
api.SetNumberOfDivisions(8);
api.CalculateStresses();
api.SaveJob(outputPath);
IsRunning = true;
// Create a new model with the super element items
api.GetFramesToCheck(out string[] sortedFrames);
var new_elements = new List<ElementModel>();
foreach (string frame in sortedFrames)
{
var beam = new BeamModel(models[0].Value.GetBeam(frame));
beam.Attributes.Clear();
beam.Loads.Clear();
beam.Results.Clear();
if (api.GetFrameStresses(frame, out string[] lc, out double[] d, out double[] n, out double[] mt,
out double[] t2, out double[] t3, out double[] m2, out double[] m3) == MeChecksConsts.Error_Success)
{
for (int i = 0; i < lc.Length; i++)
{
Common.Helpers.CsiHelper.ConvertStressesFromCSI(beam, ref m2[i], ref m3[i], ref t2[i], ref t3[i]);
var f = new BeamForceResultModel(new LoadCaseModel(i + 1, lc[i]), d[i], 0, "")
{
AxialForce = n[i],
Torque = mt[i],
ShearForce = new Vector2d(t2[i], t3[i]),
BendingMoment = new Vector2d(m2[i], m3[i])
};
beam.Results.Add(f);
}
}
new_elements.Add(beam);
//NodeModel node = beam. ///
//new_elements.Add(node);
}
var new_model = new ModelModel(models[0].Value.Units);
new_model.AddElements(new_elements);
new_model.AddLoadCombinations(load_combinations);
var new_ghmodel = new GH_Model()
{
Value = new_model
};
ModelHelper.RestoreModelGuids(models[0], ref new_ghmodel);
DA.SetData(0, new_ghmodel);
_run = false;
}
}
/// <summary>
/// Provides an Icon for the component.
/// </summary>
protected override System.Drawing.Bitmap Icon => Properties.Resources.SuperElementCheckIcon;
/// <summary>
/// Gets the unique ID for this component. Do not change this ID after release.
/// </summary>
public override Guid ComponentGuid => new("0dfc8d9c-1eeb-4368-8900-6c87ee319d2a");
}
}