3410 lines
171 KiB
C#
3410 lines
171 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using CaeMesh;
|
|
using CaeGlobals;
|
|
using System.Runtime.Serialization;
|
|
using Calculix = FileInOut.Output.Calculix;
|
|
using System.IO;
|
|
using System.Drawing;
|
|
using System.Data;
|
|
using System.Security.Cryptography;
|
|
using System.Runtime.InteropServices;
|
|
using static System.Collections.Specialized.BitVector32;
|
|
using System.Net.NetworkInformation;
|
|
using System.Xml.Linq;
|
|
using FileInOut.Input;
|
|
using CaeResults;
|
|
using System.CodeDom;
|
|
using System.Collections.Concurrent;
|
|
|
|
namespace CaeModel
|
|
{
|
|
[Serializable]
|
|
public class FeModel : ISerializable
|
|
{
|
|
// Variables
|
|
private string _hashName; //ISerializable
|
|
private FeMesh _geometry; //ISerializable
|
|
private FeMesh _mesh; //ISerializable
|
|
private EquationParameterCollection _parameters; //ISerializable
|
|
private OrderedDictionary<string, Material> _materials; //ISerializable
|
|
private OrderedDictionary<string, Section> _sections; //ISerializable
|
|
private OrderedDictionary<string, Constraint> _constraints; //ISerializable
|
|
private OrderedDictionary<string, SurfaceInteraction> _surfaceInteractions; //ISerializable
|
|
private OrderedDictionary<string, ContactPair> _contactPairs; //ISerializable
|
|
private OrderedDictionary<string, Distribution> _distributions; //ISerializable
|
|
private OrderedDictionary<string, Amplitude> _amplitudes; //ISerializable
|
|
private OrderedDictionary<string, InitialCondition> _initialConditions; //ISerializable
|
|
private StepCollection _stepCollection; //ISerializable
|
|
private OrderedDictionary<int[], Calculix.CalculixUserKeyword> _calculixUserKeywords; //ISerializable
|
|
private ModelProperties _properties; //ISerializable
|
|
private UnitSystem _unitSystem; //ISerializable
|
|
|
|
|
|
// Properties
|
|
public string Name { get; set; }
|
|
public string HashName { get { return _hashName; } }
|
|
public FeMesh Geometry { get { return _geometry; } }
|
|
public FeMesh Mesh { get { return _mesh; } }
|
|
public EquationParameterCollection Parameters { get { return _parameters; } }
|
|
public OrderedDictionary<string, Material> Materials { get { return _materials; } }
|
|
public OrderedDictionary<string, Section> Sections { get { return _sections; } }
|
|
public OrderedDictionary<string, Constraint> Constraints { get { return _constraints; } }
|
|
public OrderedDictionary<string, SurfaceInteraction> SurfaceInteractions { get { return _surfaceInteractions; } }
|
|
public OrderedDictionary<string, ContactPair> ContactPairs { get { return _contactPairs; } }
|
|
public OrderedDictionary<string, Distribution> Distributions { get { return _distributions; } }
|
|
public OrderedDictionary<string, Amplitude> Amplitudes { get { return _amplitudes; } }
|
|
public OrderedDictionary<string, InitialCondition> InitialConditions { get { return _initialConditions; } }
|
|
public StepCollection StepCollection { get { return _stepCollection; } }
|
|
public OrderedDictionary<int[], Calculix.CalculixUserKeyword> CalculixUserKeywords
|
|
{
|
|
get { return _calculixUserKeywords; }
|
|
set
|
|
{
|
|
_calculixUserKeywords = value;
|
|
}
|
|
}
|
|
public ModelProperties Properties { get { return _properties; } set { _properties = value; } }
|
|
public UnitSystem UnitSystem
|
|
{
|
|
get { return _unitSystem; }
|
|
set { _unitSystem = value; }
|
|
}
|
|
|
|
|
|
// Constructors
|
|
public FeModel(string name, UnitSystem unitSystem, OrderedDictionary<string, EquationParameter> overriddenParameters = null)
|
|
{
|
|
StringComparer sc = StringComparer.OrdinalIgnoreCase;
|
|
//
|
|
Name = name;
|
|
_hashName = Tools.GetRandomString(8);
|
|
_geometry = new FeMesh(MeshRepresentation.Geometry);
|
|
_mesh = new FeMesh(MeshRepresentation.Mesh);
|
|
_parameters = new EquationParameterCollection();
|
|
_materials = new OrderedDictionary<string, Material>("Materials", sc);
|
|
_sections = new OrderedDictionary<string, Section>("Sections", sc);
|
|
_constraints = new OrderedDictionary<string, Constraint>("Constraints", sc);
|
|
_surfaceInteractions = new OrderedDictionary<string, SurfaceInteraction>("Surface Tractions", sc);
|
|
_contactPairs = new OrderedDictionary<string, ContactPair>("Contact Pairs", sc);
|
|
_initialConditions = new OrderedDictionary<string, InitialCondition>("Initial Conditions", sc);
|
|
_distributions = new OrderedDictionary<string, Distribution>("Distributions", sc);
|
|
_amplitudes = new OrderedDictionary<string, Amplitude>("Amplitudes", sc);
|
|
_stepCollection = new StepCollection();
|
|
_properties = new ModelProperties();
|
|
if (unitSystem == null) _unitSystem = new UnitSystem();
|
|
else _unitSystem = unitSystem;
|
|
// Set overridden parameters
|
|
if (overriddenParameters != null)
|
|
{
|
|
foreach (var entry in overriddenParameters) _parameters.AddOverriddenParameter(entry.Key, entry.Value);
|
|
}
|
|
//
|
|
UpdateNCalcParameters();
|
|
}
|
|
public FeModel(SerializationInfo info, StreamingContext context)
|
|
{
|
|
StringComparer sc = StringComparer.OrdinalIgnoreCase;
|
|
// Compatibility for version v.0.6.0
|
|
_surfaceInteractions = new OrderedDictionary<string, SurfaceInteraction>("Surface Tractions", sc);
|
|
_contactPairs = new OrderedDictionary<string, ContactPair>("Contact Pairs", sc);
|
|
// Compatibility for version v.0.7.0
|
|
_unitSystem = new UnitSystem();
|
|
// Compatibility for version v.0.8.0
|
|
_hashName = Tools.GetRandomString(8);
|
|
// Compatibility for version v.1.0.0
|
|
_initialConditions = new OrderedDictionary<string, InitialCondition>("Initial Conditions", sc);
|
|
// Compatibility for version v.2.3.3
|
|
_distributions = new OrderedDictionary<string, Distribution>("Distributions", sc);
|
|
// Compatibility for version v.1.2.1
|
|
_amplitudes = new OrderedDictionary<string, Amplitude>("Amplitudes", sc);
|
|
// Compatibility for version v.1.4.0
|
|
_parameters = new EquationParameterCollection();
|
|
//
|
|
foreach (SerializationEntry entry in info)
|
|
{
|
|
switch (entry.Name)
|
|
{
|
|
case "<Name>k__BackingField": // Compatibility for version v.0.5.1
|
|
case "_name":
|
|
Name = (string)entry.Value; break;
|
|
case "_geometry":
|
|
_geometry = (FeMesh)entry.Value; break;
|
|
case "_mesh":
|
|
_mesh = (FeMesh)entry.Value; break;
|
|
case "_parameters": // Compatibility for version v.2.1.11
|
|
if (entry.Value is OrderedDictionary<string, EquationParameter> dic)
|
|
{
|
|
dic.OnDeserialization(null);
|
|
_parameters = new EquationParameterCollection(dic);
|
|
}
|
|
else _parameters = (EquationParameterCollection)entry.Value;
|
|
break;
|
|
case "_materials":
|
|
if (entry.Value is Dictionary<string, Material> md)
|
|
{
|
|
// Compatibility for version v.0.5.1
|
|
md.OnDeserialization(null);
|
|
_materials = new OrderedDictionary<string, Material>("Materials", md, sc);
|
|
}
|
|
else if (entry.Value is OrderedDictionary<string, Material> mod) _materials = mod;
|
|
else if (entry.Value == null) _materials = null;
|
|
else throw new NotSupportedException();
|
|
break;
|
|
case "_sections":
|
|
if (entry.Value is Dictionary<string, Section> sd)
|
|
{
|
|
// Compatibility for version v.0.5.1
|
|
sd.OnDeserialization(null);
|
|
_sections = new OrderedDictionary<string, Section>("Sections", sd, sc);
|
|
}
|
|
else if (entry.Value is OrderedDictionary<string, Section> sod) _sections = sod;
|
|
else if (entry.Value == null) _sections = null;
|
|
else throw new NotSupportedException();
|
|
break;
|
|
case "_constraints":
|
|
if (entry.Value is Dictionary<string, Constraint> cd)
|
|
{
|
|
// Compatibility for version v.0.5.1
|
|
cd.OnDeserialization(null);
|
|
_constraints = new OrderedDictionary<string, Constraint>("Constraints", cd, sc);
|
|
}
|
|
else if (entry.Value is OrderedDictionary<string, Constraint> cod) _constraints = cod;
|
|
else if (entry.Value == null) _constraints = null;
|
|
else throw new NotSupportedException();
|
|
break;
|
|
case "_surfaceInteractions":
|
|
_surfaceInteractions = (OrderedDictionary<string, SurfaceInteraction>)entry.Value; break;
|
|
case "_contactPairs":
|
|
_contactPairs = (OrderedDictionary<string, ContactPair>)entry.Value; break;
|
|
case "_distributions":
|
|
_distributions = (OrderedDictionary<string, Distribution>)entry.Value; break;
|
|
case "_amplitudes":
|
|
_amplitudes = (OrderedDictionary<string, Amplitude>)entry.Value; break;
|
|
case "_initialConditions":
|
|
_initialConditions = (OrderedDictionary<string, InitialCondition>)entry.Value; break;
|
|
case "_stepCollection":
|
|
_stepCollection = (StepCollection)entry.Value; break;
|
|
case "_calculixUserKeywords":
|
|
if (entry.Value is Dictionary<int[], Calculix.CalculixUserKeyword> cukd)
|
|
{
|
|
// Compatibility for version v.0.5.1
|
|
cukd.OnDeserialization(null);
|
|
_calculixUserKeywords = new OrderedDictionary<int[], Calculix.CalculixUserKeyword>("Keywords", cukd);
|
|
}
|
|
else if (entry.Value is OrderedDictionary<int[], Calculix.CalculixUserKeyword> cukod)
|
|
_calculixUserKeywords = cukod;
|
|
else if (entry.Value == null) _calculixUserKeywords = null;
|
|
else throw new NotSupportedException();
|
|
break;
|
|
case "_properties":
|
|
_properties = (ModelProperties)entry.Value;
|
|
break;
|
|
case "_unitSystem":
|
|
_unitSystem = (UnitSystem)entry.Value; break;
|
|
case "_hashName":
|
|
_hashName = (string)entry.Value; break;
|
|
default:
|
|
throw new NotSupportedException();
|
|
}
|
|
}
|
|
//
|
|
_unitSystem.SetConverterUnits();
|
|
//
|
|
// Call it to load the dictionary immediately
|
|
//_parameters.OnDeserialization(null);
|
|
//_geometry.Parts.OnDeserialization(null);
|
|
//_mesh.Parts.OnDeserialization(null);
|
|
//UpdateNCalcParameters(); // is called after the data is read from binary reader
|
|
}
|
|
|
|
|
|
// Methods
|
|
public static void WriteToBinaryWriter(FeModel model, BinaryWriter bw)
|
|
{
|
|
// Write geometry
|
|
if (model == null || model.Geometry == null)
|
|
{
|
|
bw.Write((int)0);
|
|
}
|
|
else
|
|
{
|
|
bw.Write((int)1);
|
|
FeMesh.WriteToBinaryWriter(model.Geometry, bw);
|
|
}
|
|
// Write mesh
|
|
if (model == null || model.Mesh == null)
|
|
{
|
|
bw.Write((int)0);
|
|
}
|
|
else
|
|
{
|
|
bw.Write((int)1);
|
|
FeMesh.WriteToBinaryWriter(model.Mesh, bw);
|
|
}
|
|
}
|
|
public static void ReadFromBinaryReader(FeModel model, BinaryReader br, int version)
|
|
{
|
|
// Read geometry
|
|
int geometryExists = br.ReadInt32();
|
|
if (geometryExists == 1)
|
|
{
|
|
FeMesh.ReadFromBinaryReader(model.Geometry, br, version);
|
|
}
|
|
// Read mesh
|
|
int meshExists = br.ReadInt32();
|
|
if (meshExists == 1)
|
|
{
|
|
FeMesh.ReadFromBinaryReader(model.Mesh, br, version);
|
|
}
|
|
}
|
|
// Check
|
|
public string[] CheckValidity(List<Tuple<NamedClass, string>> items)
|
|
{
|
|
// Tuple<NamedClass, string> ... Tuple<invalidItem, stepName>
|
|
if (_mesh == null) return new string[0];
|
|
//
|
|
List<string> invalidItems = new List<string>();
|
|
bool valid = false;
|
|
invalidItems.AddRange(_geometry.CheckValidity(items, IsMeshSetupItemProperlyDefined));
|
|
invalidItems.AddRange(_mesh.CheckValidity(items, IsMeshSetupItemProperlyDefined));
|
|
// Materials
|
|
Material material;
|
|
foreach (var entry in _materials)
|
|
{
|
|
material = entry.Value;
|
|
valid = true;
|
|
foreach (var property in material.Properties)
|
|
{
|
|
// Check equations
|
|
valid &= property.TryCheckEquations();
|
|
}
|
|
//
|
|
SetItemValidity(null, material, valid, items);
|
|
if (!valid && material.Active) invalidItems.Add("Material: " + material.Name);
|
|
}
|
|
// Sections
|
|
Section section;
|
|
foreach (var entry in _sections)
|
|
{
|
|
section = entry.Value;
|
|
// Region
|
|
valid = IsSectionRegionValid(section);
|
|
//
|
|
if (section is SolidSection || section is ShellSection || section is MembraneSection)
|
|
valid &= _materials.ContainsValidKey(section.MaterialName);
|
|
else if (section is PointMassSection || section is DistributedMassSection) { }
|
|
else throw new NotSupportedException();
|
|
// Check equations
|
|
valid &= section.TryCheckEquations();
|
|
//
|
|
SetItemValidity(null, section, valid, items);
|
|
if (!valid && section.Active) invalidItems.Add("Section: " + section.Name);
|
|
}
|
|
// Constraints
|
|
Constraint constraint;
|
|
foreach (var entry in _constraints)
|
|
{
|
|
constraint = entry.Value;
|
|
// Region
|
|
valid = IsConstraintRegionValid(constraint);
|
|
// Check equations
|
|
valid &= constraint.TryCheckEquations();
|
|
//
|
|
SetItemValidity(null, constraint, valid, items);
|
|
if (!valid && constraint.Active) invalidItems.Add("Constraint: " + constraint.Name);
|
|
}
|
|
// Contact pairs
|
|
ContactPair contactPair;
|
|
foreach (var entry in _contactPairs)
|
|
{
|
|
contactPair = entry.Value;
|
|
valid = _surfaceInteractions.ContainsValidKey(contactPair.SurfaceInteractionName)
|
|
&& _mesh.Surfaces.ContainsValidKey(contactPair.SlaveRegionName)
|
|
&& _mesh.Surfaces.ContainsValidKey(contactPair.MasterRegionName);
|
|
//
|
|
SetItemValidity(null, contactPair, valid, items);
|
|
if (!valid && contactPair.Active) invalidItems.Add("Contact pair: " + contactPair.Name);
|
|
}
|
|
// Distributions
|
|
Distribution distribution;
|
|
foreach (var entry in _distributions)
|
|
{
|
|
distribution = entry.Value;
|
|
valid = true;
|
|
// Coordinate system
|
|
if (distribution.CoordinateSystemName != CaeMesh.CoordinateSystem.DefaultCoordinateSystemName &&
|
|
!_mesh.CoordinateSystems.ContainsValidKey(distribution.CoordinateSystemName)) valid = false;
|
|
//
|
|
SetItemValidity(null, distribution, valid, items);
|
|
if (!valid && distribution.Active) invalidItems.Add("Distribution: " + distribution.Name);
|
|
}
|
|
// Initial conditions
|
|
InitialCondition initialCondition;
|
|
foreach (var entry in _initialConditions)
|
|
{
|
|
initialCondition = entry.Value;
|
|
// Region
|
|
valid = IsInitialConditionRegionValid(initialCondition);
|
|
// Distribution
|
|
if (initialCondition is IDistribution icd)
|
|
{
|
|
if (icd.DistributionName != Distribution.DefaultDistributionName &&
|
|
!_distributions.ContainsValidKey(icd.DistributionName)) valid = false;
|
|
}
|
|
// Check equations
|
|
valid &= initialCondition.TryCheckEquations();
|
|
//
|
|
SetItemValidity(null, initialCondition, valid, items);
|
|
if (!valid && initialCondition.Active) invalidItems.Add("Initial condition: " + initialCondition.Name);
|
|
}
|
|
// Steps
|
|
HistoryOutput historyOutput;
|
|
BoundaryCondition bc;
|
|
Load load;
|
|
DefinedField definedField;
|
|
//
|
|
foreach (var step in _stepCollection.StepsList)
|
|
{
|
|
// History output
|
|
foreach (var hoEntry in step.HistoryOutputs)
|
|
{
|
|
historyOutput = hoEntry.Value;
|
|
// Region
|
|
valid = IsHistoryOutputRegionValid(historyOutput);
|
|
//
|
|
SetItemValidity(step.Name, historyOutput, valid, items);
|
|
if (!valid && historyOutput.Active) invalidItems.Add("History output: " + step.Name + ", " + historyOutput.Name);
|
|
}
|
|
// Boundary conditions
|
|
foreach (var bcEntry in step.BoundaryConditions)
|
|
{
|
|
bc = bcEntry.Value;
|
|
// Region
|
|
valid = IsBoundaryConditionRegionValid(bc);
|
|
// Distribution
|
|
if (bc is IDistribution bcd)
|
|
{
|
|
if (bcd.DistributionName != Distribution.DefaultDistributionName &&
|
|
!_distributions.ContainsValidKey(bcd.DistributionName)) valid = false;
|
|
}
|
|
// Amplitude
|
|
if (bc.AmplitudeName != Amplitude.DefaultAmplitudeName &&
|
|
!_amplitudes.ContainsValidKey(bc.AmplitudeName)) valid = false;
|
|
// Coordinate system
|
|
if (bc.CoordinateSystemName != CaeMesh.CoordinateSystem.DefaultCoordinateSystemName &&
|
|
!_mesh.CoordinateSystems.ContainsValidKey(bc.CoordinateSystemName)) valid = false;
|
|
// Check equations
|
|
valid &= bc.TryCheckEquations();
|
|
//
|
|
SetItemValidity(step.Name, bc, valid, items);
|
|
if (!valid && bc.Active) invalidItems.Add("Boundary condition: " + step.Name + ", " + bc.Name);
|
|
}
|
|
// Loads
|
|
foreach (var loadEntry in step.Loads)
|
|
{
|
|
load = loadEntry.Value;
|
|
// Region
|
|
valid = IsLoadRegionValid(load);
|
|
// D2 vs axisymmetric
|
|
if (valid && load is CentrifLoad cf)
|
|
{
|
|
if (_properties.ModelSpace == ModelSpaceEnum.PlaneStress ||
|
|
_properties.ModelSpace == ModelSpaceEnum.PlaneStrain)
|
|
{
|
|
if (cf.Axisymmetric == true) valid = false;
|
|
}
|
|
else if (_properties.ModelSpace == ModelSpaceEnum.Axisymmetric)
|
|
{
|
|
if (cf.Axisymmetric == false) valid = false;
|
|
}
|
|
}
|
|
// Distribution
|
|
if (load is IDistribution lwd)
|
|
{
|
|
if (lwd.DistributionName != Distribution.DefaultDistributionName &&
|
|
!_distributions.ContainsValidKey(lwd.DistributionName)) valid = false;
|
|
}
|
|
// Amplitude
|
|
if (load.AmplitudeName != Amplitude.DefaultAmplitudeName &&
|
|
!_amplitudes.ContainsValidKey(load.AmplitudeName)) valid = false;
|
|
// Coordinate system
|
|
if (load.CoordinateSystemName != CoordinateSystem.DefaultCoordinateSystemName &&
|
|
!_mesh.CoordinateSystems.ContainsValidKey(load.CoordinateSystemName)) valid = false;
|
|
// Check equations
|
|
valid &= load.TryCheckEquations();
|
|
//
|
|
SetItemValidity(step.Name, load, valid, items);
|
|
if (!valid && load.Active) invalidItems.Add("Load: " + step.Name + ", " + load.Name);
|
|
}
|
|
// Defined fields
|
|
foreach (var fieldEntry in step.DefinedFields)
|
|
{
|
|
definedField = fieldEntry.Value;
|
|
// Region
|
|
valid = IsDefinedFieldRegionValid(definedField);
|
|
// Distribution
|
|
if (definedField is IDistribution dfd)
|
|
{
|
|
if (dfd.DistributionName != Distribution.DefaultDistributionName &&
|
|
!_distributions.ContainsValidKey(dfd.DistributionName)) valid = false;
|
|
}
|
|
// Check equations
|
|
valid &= definedField.TryCheckEquations();
|
|
//
|
|
SetItemValidity(step.Name, definedField, valid, items);
|
|
if (!valid && definedField.Active) invalidItems.Add("Defined field: " + step.Name + ", " + definedField.Name);
|
|
}
|
|
}
|
|
//
|
|
return invalidItems.ToArray();
|
|
}
|
|
private void Check2D(FeMesh mesh)
|
|
{
|
|
// Check for 2D geometry - the same check in ImportGeometry
|
|
if (_properties.ModelSpace.IsTwoD())
|
|
{
|
|
if (!mesh.BoundingBox.Is2D())
|
|
throw new CaeException("The selected file does not contain 2D geometry in x-y plane.");
|
|
else if (!Check2DSurfaceNormals(mesh))
|
|
{
|
|
MessageBoxes.ShowWarning("The imported geometry contains faces oriented in the wrong direction (negative Z). " +
|
|
"To use them in the analysis, change their orientation.");
|
|
}
|
|
}
|
|
}
|
|
private bool Check2DSurfaceNormals(FeMesh mesh)
|
|
{
|
|
int cellId;
|
|
int[] cell;
|
|
FeNode normal;
|
|
foreach (var entry in mesh.Parts)
|
|
{
|
|
for (int i = 0; i < entry.Value.Visualization.CellIdsByFace.Length; i++)
|
|
{
|
|
if (entry.Value.Visualization.CellIdsByFace[i].Length > 0)
|
|
{
|
|
cellId = entry.Value.Visualization.CellIdsByFace[i][0];
|
|
cell = entry.Value.Visualization.Cells[cellId];
|
|
normal = mesh.ComputeNormalFromFaceCellIndices(cell);
|
|
if (normal.Z < 0) return false;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
public bool IsSectionRegionValid(Section section)
|
|
{
|
|
bool valid;
|
|
//
|
|
if (section is SolidSection ss)
|
|
{
|
|
valid =
|
|
(ss.RegionType == RegionTypeEnum.PartName && _mesh.Parts.ContainsValidKey(ss.RegionName))
|
|
|| (ss.RegionType == RegionTypeEnum.ElementSetName && _mesh.ElementSets.ContainsValidKey(ss.RegionName));
|
|
}
|
|
else if (section is ShellSection shs)
|
|
{
|
|
valid =
|
|
(shs.RegionType == RegionTypeEnum.PartName && _mesh.Parts.ContainsValidKey(shs.RegionName))
|
|
|| (shs.RegionType == RegionTypeEnum.ElementSetName && _mesh.ElementSets.ContainsValidKey(shs.RegionName));
|
|
}
|
|
else if (section is MembraneSection ms)
|
|
{
|
|
valid =
|
|
(ms.RegionType == RegionTypeEnum.PartName && _mesh.Parts.ContainsValidKey(ms.RegionName))
|
|
|| (ms.RegionType == RegionTypeEnum.ElementSetName && _mesh.ElementSets.ContainsValidKey(ms.RegionName));
|
|
}
|
|
else if (section is PointMassSection pms)
|
|
{
|
|
valid =
|
|
(pms.RegionType == RegionTypeEnum.NodeSetName && _mesh.NodeSets.ContainsValidKey(pms.RegionName))
|
|
|| (pms.RegionType == RegionTypeEnum.ReferencePointName &&
|
|
_mesh.ReferencePoints.ContainsValidKey(pms.RegionName));
|
|
}
|
|
else if (section is DistributedMassSection dms)
|
|
{
|
|
valid = dms.RegionType == RegionTypeEnum.SurfaceName && _mesh.Surfaces.ContainsValidKey(dms.RegionName);
|
|
}
|
|
else throw new NotSupportedException();
|
|
//
|
|
return valid;
|
|
}
|
|
public bool IsInitialConditionRegionValid(InitialCondition initialCondition)
|
|
{
|
|
bool valid;
|
|
//
|
|
if (initialCondition is InitialTemperature it)
|
|
{
|
|
valid = (it.RegionType == RegionTypeEnum.NodeSetName && _mesh.NodeSets.ContainsValidKey(it.RegionName)) ||
|
|
(it.RegionType == RegionTypeEnum.SurfaceName && _mesh.Surfaces.ContainsValidKey(it.RegionName));
|
|
}
|
|
else if (initialCondition is InitialTranslationalVelocity itv)
|
|
{
|
|
valid = (itv.RegionType == RegionTypeEnum.NodeSetName && _mesh.NodeSets.ContainsValidKey(itv.RegionName)) ||
|
|
(itv.RegionType == RegionTypeEnum.SurfaceName && _mesh.Surfaces.ContainsValidKey(itv.RegionName)) ||
|
|
(itv.RegionType == RegionTypeEnum.ReferencePointName
|
|
&& _mesh.ReferencePoints.ContainsValidKey(itv.RegionName));
|
|
}
|
|
else if (initialCondition is InitialAngularVelocity iav)
|
|
{
|
|
valid = (iav.RegionType == RegionTypeEnum.NodeSetName && _mesh.NodeSets.ContainsValidKey(iav.RegionName)) ||
|
|
(iav.RegionType == RegionTypeEnum.SurfaceName && _mesh.Surfaces.ContainsValidKey(iav.RegionName)) ||
|
|
(iav.RegionType == RegionTypeEnum.ReferencePointName
|
|
&& _mesh.ReferencePoints.ContainsValidKey(iav.RegionName));
|
|
}
|
|
else throw new NotSupportedException();
|
|
//
|
|
return valid;
|
|
}
|
|
public bool IsHistoryOutputRegionValid(HistoryOutput historyOutput)
|
|
{
|
|
bool valid;
|
|
//
|
|
if (historyOutput is NodalHistoryOutput nho)
|
|
{
|
|
valid = (nho.RegionType == RegionTypeEnum.NodeSetName && _mesh.NodeSets.ContainsValidKey(nho.RegionName))
|
|
|| (nho.RegionType == RegionTypeEnum.SurfaceName && _mesh.Surfaces.ContainsValidKey(nho.RegionName))
|
|
|| (nho.RegionType == RegionTypeEnum.ReferencePointName
|
|
&& _mesh.ReferencePoints.ContainsValidKey(nho.RegionName));
|
|
}
|
|
else if (historyOutput is ElementHistoryOutput eho)
|
|
{
|
|
valid = _mesh.ElementSets.ContainsValidKey(eho.RegionName);
|
|
}
|
|
else if (historyOutput is ContactHistoryOutput cho)
|
|
{
|
|
valid = _contactPairs.ContainsValidKey(cho.RegionName);
|
|
}
|
|
else throw new NotSupportedException();
|
|
//
|
|
return valid;
|
|
}
|
|
public bool IsConstraintRegionValid(Constraint constraint)
|
|
{
|
|
bool valid;
|
|
//
|
|
if (constraint is PointSpring ps)
|
|
{
|
|
valid = (ps.RegionType == RegionTypeEnum.NodeSetName && _mesh.NodeSets.ContainsValidKey(ps.RegionName))
|
|
|| (ps.RegionType == RegionTypeEnum.ReferencePointName
|
|
&& _mesh.ReferencePoints.ContainsValidKey(ps.RegionName));
|
|
}
|
|
else if (constraint is SurfaceSpring ss)
|
|
{
|
|
valid = ss.RegionType == RegionTypeEnum.SurfaceName && _mesh.Surfaces.ContainsValidKey(ss.RegionName);
|
|
}
|
|
else if (constraint is CompressionOnly co)
|
|
{
|
|
valid = co.RegionType == RegionTypeEnum.SurfaceName && _mesh.Surfaces.ContainsValidKey(co.RegionName);
|
|
}
|
|
else if (constraint is RigidBody rb)
|
|
{
|
|
valid = (rb.ReferencePointName != null && _mesh.ReferencePoints.ContainsValidKey(rb.ReferencePointName))
|
|
&& ((rb.RegionType == RegionTypeEnum.NodeSetName && _mesh.NodeSets.ContainsValidKey(rb.RegionName))
|
|
|| (rb.RegionType == RegionTypeEnum.SurfaceName && _mesh.Surfaces.ContainsValidKey(rb.RegionName)));
|
|
}
|
|
else if (constraint is Tie t)
|
|
{
|
|
valid = _mesh.Surfaces.ContainsValidKey(t.SlaveRegionName) && _mesh.Surfaces.ContainsValidKey(t.MasterRegionName)
|
|
&& (t.SlaveRegionName != t.MasterRegionName);
|
|
}
|
|
else throw new NotSupportedException();
|
|
//
|
|
return valid;
|
|
}
|
|
public bool IsBoundaryConditionRegionValid(BoundaryCondition bc)
|
|
{
|
|
bool valid;
|
|
//
|
|
if (bc is FixedBC fix)
|
|
{
|
|
valid = (fix.RegionType == RegionTypeEnum.NodeSetName && _mesh.NodeSets.ContainsValidKey(fix.RegionName))
|
|
|| (fix.RegionType == RegionTypeEnum.SurfaceName && (_mesh.Surfaces.ContainsValidKey(fix.RegionName)))
|
|
|| (fix.RegionType == RegionTypeEnum.ReferencePointName
|
|
&& (_mesh.ReferencePoints.ContainsValidKey(fix.RegionName)));
|
|
}
|
|
else if (bc is DisplacementRotation dr)
|
|
{
|
|
valid = (dr.RegionType == RegionTypeEnum.NodeSetName && _mesh.NodeSets.ContainsValidKey(dr.RegionName))
|
|
|| (dr.RegionType == RegionTypeEnum.SurfaceName && (_mesh.Surfaces.ContainsValidKey(dr.RegionName)))
|
|
|| (dr.RegionType == RegionTypeEnum.ReferencePointName
|
|
&& (_mesh.ReferencePoints.ContainsValidKey(dr.RegionName)));
|
|
}
|
|
else if (bc is SubmodelBC sm)
|
|
{
|
|
valid = (sm.RegionType == RegionTypeEnum.NodeSetName && _mesh.NodeSets.ContainsValidKey(sm.RegionName))
|
|
|| (sm.RegionType == RegionTypeEnum.SurfaceName && (_mesh.Surfaces.ContainsValidKey(sm.RegionName)));
|
|
}
|
|
else if (bc is TemperatureBC tmp)
|
|
{
|
|
valid = (tmp.RegionType == RegionTypeEnum.NodeSetName && _mesh.NodeSets.ContainsValidKey(tmp.RegionName))
|
|
|| (tmp.RegionType == RegionTypeEnum.SurfaceName && (_mesh.Surfaces.ContainsValidKey(tmp.RegionName)));
|
|
}
|
|
else throw new NotSupportedException();
|
|
//
|
|
return valid;
|
|
}
|
|
public bool IsLoadRegionValid(Load load)
|
|
{
|
|
bool valid;
|
|
FeSurface s;
|
|
//
|
|
if (load is CLoad cl)
|
|
{
|
|
valid = (cl.RegionType == RegionTypeEnum.NodeSetName && _mesh.NodeSets.ContainsValidKey(cl.RegionName))
|
|
|| (cl.RegionType == RegionTypeEnum.ReferencePointName
|
|
&& (_mesh.ReferencePoints.ContainsValidKey(cl.RegionName)));
|
|
}
|
|
else if (load is MomentLoad ml)
|
|
{
|
|
valid = (ml.RegionType == RegionTypeEnum.NodeSetName && _mesh.NodeSets.ContainsValidKey(ml.RegionName))
|
|
|| (ml.RegionType == RegionTypeEnum.ReferencePointName
|
|
&& (_mesh.ReferencePoints.ContainsValidKey(ml.RegionName)));
|
|
}
|
|
else if (load is DLoad dl)
|
|
{
|
|
valid = (_mesh.Surfaces.TryGetValue(dl.RegionName, out s) && s.Valid);
|
|
}
|
|
else if (load is HydrostaticPressure hpl)
|
|
{
|
|
valid = (_mesh.Surfaces.TryGetValue(hpl.SurfaceName, out s) && s.Valid);
|
|
}
|
|
else if (load is ImportedPressure ip)
|
|
{
|
|
valid = (_mesh.Surfaces.TryGetValue(ip.SurfaceName, out s) && s.Valid);
|
|
}
|
|
else if (load is STLoad stl)
|
|
{
|
|
valid = (_mesh.Surfaces.TryGetValue(stl.SurfaceName, out s) && s.Valid);
|
|
}
|
|
else if (load is ImportedSTLoad istl)
|
|
{
|
|
valid = (_mesh.Surfaces.TryGetValue(istl.SurfaceName, out s) && s.Valid);
|
|
}
|
|
else if (load is ShellEdgeLoad sel)
|
|
{
|
|
valid = (_mesh.Surfaces.TryGetValue(sel.SurfaceName, out s) && s.Valid);
|
|
}
|
|
else if (load is GravityLoad gl)
|
|
{
|
|
valid = (gl.RegionType == RegionTypeEnum.PartName && _mesh.Parts.ContainsValidKey(gl.RegionName)) ||
|
|
(gl.RegionType == RegionTypeEnum.ElementSetName && _mesh.ElementSets.ContainsValidKey(gl.RegionName)) ||
|
|
(gl.RegionType == RegionTypeEnum.MassSection && _sections.ContainsValidKey(gl.RegionName)) ||
|
|
(gl.RegionType == RegionTypeEnum.Selection && _mesh.GetPartNamesFromPartIds(gl.CreationIds) != null &&
|
|
_mesh.GetPartNamesFromPartIds(gl.CreationIds).Length == gl.CreationIds.Length);
|
|
}
|
|
else if (load is CentrifLoad cf)
|
|
{
|
|
valid = (cf.RegionType == RegionTypeEnum.PartName && _mesh.Parts.ContainsValidKey(cf.RegionName)) ||
|
|
(cf.RegionType == RegionTypeEnum.ElementSetName && _mesh.ElementSets.ContainsValidKey(cf.RegionName)) ||
|
|
(cf.RegionType == RegionTypeEnum.MassSection && _sections.ContainsValidKey(cf.RegionName)) ||
|
|
(cf.RegionType == RegionTypeEnum.Selection && _mesh.GetPartNamesFromPartIds(cf.CreationIds) != null &&
|
|
_mesh.GetPartNamesFromPartIds(cf.CreationIds).Length == cf.CreationIds.Length);
|
|
}
|
|
else if (load is PreTensionLoad ptl)
|
|
{
|
|
valid = (_mesh.Surfaces.TryGetValue(ptl.SurfaceName, out s) && s.Valid && s.Type == FeSurfaceType.Element);
|
|
}
|
|
// Thermal
|
|
else if (load is CFlux cfl)
|
|
{
|
|
valid = (cfl.RegionType == RegionTypeEnum.NodeSetName && _mesh.NodeSets.ContainsValidKey(cfl.RegionName));
|
|
}
|
|
else if (load is DFlux df)
|
|
{
|
|
valid = (_mesh.Surfaces.TryGetValue(df.SurfaceName, out s) && s.Valid);
|
|
}
|
|
else if (load is BodyFlux bf)
|
|
{
|
|
valid = (bf.RegionType == RegionTypeEnum.PartName && _mesh.Parts.ContainsValidKey(bf.RegionName))
|
|
|| (bf.RegionType == RegionTypeEnum.ElementSetName
|
|
&& _mesh.ElementSets.ContainsValidKey(bf.RegionName)
|
|
|| (bf.RegionType == RegionTypeEnum.Selection && _mesh.GetPartNamesFromPartIds(bf.CreationIds) != null &&
|
|
_mesh.GetPartNamesFromPartIds(bf.CreationIds).Length == bf.CreationIds.Length));
|
|
}
|
|
else if (load is FilmHeatTransfer fht)
|
|
{
|
|
valid = (_mesh.Surfaces.TryGetValue(fht.SurfaceName, out s) && s.Valid);
|
|
}
|
|
else if (load is RadiationHeatTransfer rht)
|
|
{
|
|
valid = (_mesh.Surfaces.TryGetValue(rht.SurfaceName, out s) && s.Valid);
|
|
}
|
|
else throw new NotSupportedException();
|
|
//
|
|
return valid;
|
|
}
|
|
public bool IsDefinedFieldRegionValid(DefinedField definedField)
|
|
{
|
|
bool valid;
|
|
//
|
|
if (definedField is DefinedTemperature dt)
|
|
{
|
|
if (dt.Type == DefinedTemperatureTypeEnum.ByValue)
|
|
{
|
|
valid = (dt.RegionType == RegionTypeEnum.NodeSetName && _mesh.NodeSets.ContainsValidKey(dt.RegionName)) ||
|
|
(dt.RegionType == RegionTypeEnum.SurfaceName && _mesh.Surfaces.ContainsValidKey(dt.RegionName));
|
|
}
|
|
else if (dt.Type == DefinedTemperatureTypeEnum.FromFile)
|
|
{
|
|
// Defined field created from file needs no selection
|
|
valid = true;
|
|
}
|
|
else throw new NotSupportedException();
|
|
}
|
|
else throw new NotSupportedException();
|
|
//
|
|
return valid;
|
|
}
|
|
private void SetItemValidity(string stepName, NamedClass item, bool validity, List<Tuple<NamedClass, string>> items)
|
|
{
|
|
if (item.Valid != validity)
|
|
{
|
|
item.Valid = validity;
|
|
items.Add(new Tuple<NamedClass, string>(item, stepName));
|
|
}
|
|
}
|
|
public bool RegionValid(IMultiRegion multiRegion)
|
|
{
|
|
if (multiRegion.RegionType == RegionTypeEnum.NodeSetName && _mesh.NodeSets.ContainsKey(multiRegion.RegionName))
|
|
return _mesh.NodeSets[multiRegion.RegionName].Valid;
|
|
else if (multiRegion.RegionType == RegionTypeEnum.ElementSetName && _mesh.ElementSets.ContainsKey(multiRegion.RegionName))
|
|
return _mesh.ElementSets[multiRegion.RegionName].Valid;
|
|
else if (multiRegion.RegionType == RegionTypeEnum.SurfaceName && _mesh.Surfaces.ContainsKey(multiRegion.RegionName))
|
|
return _mesh.Surfaces[multiRegion.RegionName].Valid;
|
|
//
|
|
return false;
|
|
}
|
|
//
|
|
public int[] GetSectionAssignments(out Dictionary<int, int> elementIdSectionId)
|
|
{
|
|
int sectionId = 0;
|
|
elementIdSectionId = new Dictionary<int, int>();
|
|
//
|
|
foreach (var entry in _sections)
|
|
{
|
|
if (entry.Value is MassSection) continue;
|
|
//
|
|
if (entry.Value.RegionType == RegionTypeEnum.PartName)
|
|
{
|
|
foreach (var elementId in _mesh.Parts[entry.Value.RegionName].Labels)
|
|
{
|
|
if (elementIdSectionId.ContainsKey(elementId)) elementIdSectionId[elementId] = sectionId;
|
|
else elementIdSectionId.Add(elementId, sectionId);
|
|
}
|
|
}
|
|
else if (entry.Value.RegionType == RegionTypeEnum.ElementSetName)
|
|
{
|
|
if (entry.Value is SolidSection)
|
|
{
|
|
FeElementSet elementSet = _mesh.ElementSets[entry.Value.RegionName];
|
|
if (elementSet.CreatedFromParts)
|
|
{
|
|
string[] partNames = _mesh.GetPartNamesFromPartIds(elementSet.Labels);
|
|
foreach (var partName in partNames)
|
|
{
|
|
foreach (var elementId in _mesh.Parts[partName].Labels)
|
|
{
|
|
if (elementIdSectionId.ContainsKey(elementId)) elementIdSectionId[elementId] = sectionId;
|
|
else elementIdSectionId.Add(elementId, sectionId);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
foreach (var elementId in elementSet.Labels)
|
|
{
|
|
if (elementIdSectionId.ContainsKey(elementId)) elementIdSectionId[elementId] = sectionId;
|
|
else elementIdSectionId.Add(elementId, sectionId);
|
|
}
|
|
}
|
|
}
|
|
else if (entry.Value is ShellSection || entry.Value is MembraneSection)
|
|
{
|
|
FeElementSet elementSet = _mesh.ElementSets[entry.Value.RegionName];
|
|
foreach (var elementId in elementSet.Labels)
|
|
{
|
|
if (elementIdSectionId.ContainsKey(elementId)) elementIdSectionId[elementId] = sectionId;
|
|
else elementIdSectionId.Add(elementId, sectionId);
|
|
}
|
|
}
|
|
else throw new NotSupportedException();
|
|
}
|
|
else throw new NotSupportedException();
|
|
//
|
|
sectionId++;
|
|
}
|
|
// Not assigned
|
|
IEnumerable<int> unAssignedElementIds = _mesh.Elements.Keys.Except(elementIdSectionId.Keys);
|
|
int[] unAssignedElementIdsArray = new int[unAssignedElementIds.Count()];
|
|
int count = 0;
|
|
foreach (var elementId in unAssignedElementIds)
|
|
{
|
|
elementIdSectionId.Add(elementId, -1);
|
|
unAssignedElementIdsArray[count++] = elementId;
|
|
}
|
|
//
|
|
return unAssignedElementIdsArray;
|
|
}
|
|
public void GetMaterialAssignments(out Dictionary<int, int> elementIdMaterialId)
|
|
{
|
|
// Get element section ids
|
|
Dictionary<int, int> elementIdSectionId;
|
|
GetSectionAssignments(out elementIdSectionId);
|
|
// Get material ids
|
|
int count = 0;
|
|
Dictionary<string, int> materialId = new Dictionary<string, int>();
|
|
foreach (var entry in _materials) materialId.Add(entry.Value.Name, count++);
|
|
// Get a map of section materials
|
|
count = 0;
|
|
Dictionary<int, int> sectionIdMaterialId = new Dictionary<int, int>();
|
|
sectionIdMaterialId.Add(-1, -1); // add missing section
|
|
foreach (var entry in _sections) sectionIdMaterialId.Add(count++, materialId[entry.Value.MaterialName]);
|
|
// Use the map
|
|
elementIdMaterialId = new Dictionary<int, int>();
|
|
foreach (var entry in elementIdSectionId) elementIdMaterialId.Add(entry.Key, sectionIdMaterialId[entry.Value]);
|
|
}
|
|
public void GetNodalSafetyFactorLimits()
|
|
{
|
|
Dictionary<int, int> elementIdMaterialId;
|
|
GetMaterialAssignments(out elementIdMaterialId);
|
|
//
|
|
int count = 0;
|
|
Dictionary<int, double> materialIdSafetyFactor = new Dictionary<int, double>();
|
|
foreach (var entry in _materials)
|
|
{
|
|
//if (safetyFactor != null) materialIdSafetyFactor.Add(count++, safetyFactor.SafetyFactorLimitValue);
|
|
//else materialIdSafetyFactor.Add(count++, double.NaN);
|
|
}
|
|
//
|
|
Dictionary<int, double> nodeIdSafetyFactorLimit = new Dictionary<int, double>();
|
|
}
|
|
public bool AreSlipWearCoefficientsDefined(out Dictionary<int, double> materialIdCoefficient)
|
|
{
|
|
int count = 0;
|
|
bool containsWear = false;
|
|
double coefficient;
|
|
materialIdCoefficient = new Dictionary<int, double>();
|
|
// For each material check if the material has wear coefficients defined
|
|
foreach (var entry in _materials)
|
|
{
|
|
coefficient = 0;
|
|
foreach (var property in entry.Value.Properties)
|
|
{
|
|
if (property is SlipWear sw)
|
|
{
|
|
coefficient = sw.WearCoefficient.Value / sw.Hardness.Value * _properties.CyclesIncrement;
|
|
containsWear = true;
|
|
break;
|
|
}
|
|
}
|
|
//
|
|
materialIdCoefficient.Add(count++, coefficient);
|
|
}
|
|
return containsWear;
|
|
}
|
|
public Dictionary<int, double> GetNodalSlipWearCoefficients()
|
|
{
|
|
double coefficient;
|
|
Dictionary<int, double> materialIdCoefficient;
|
|
bool containsWear = AreSlipWearCoefficientsDefined(out materialIdCoefficient);
|
|
// If wear coefficients are defined
|
|
if (containsWear)
|
|
{
|
|
Dictionary<int, int> elementIdMaterialId;
|
|
GetMaterialAssignments(out elementIdMaterialId);
|
|
// Get wear coefficient for each element
|
|
Dictionary<int, double> elementIdCoefficient = new Dictionary<int, double>();
|
|
foreach (var entry in elementIdMaterialId)
|
|
{
|
|
elementIdCoefficient.Add(entry.Key, materialIdCoefficient[entry.Value]);
|
|
}
|
|
// Get wear coefficients for each node
|
|
HashSet<double> allCoefficients;
|
|
Dictionary<int, HashSet<double>> nodeIdAllCoefficients = new Dictionary<int, HashSet<double>>();
|
|
FeElement element;
|
|
foreach (var entry in elementIdCoefficient)
|
|
{
|
|
element = _mesh.Elements[entry.Key];
|
|
foreach (var nodeId in element.NodeIds)
|
|
{
|
|
if (nodeIdAllCoefficients.TryGetValue(nodeId, out allCoefficients)) allCoefficients.Add(entry.Value);
|
|
else nodeIdAllCoefficients.Add(nodeId, new HashSet<double>() { entry.Value });
|
|
}
|
|
}
|
|
// Compute the average
|
|
Dictionary<int, double> nodeIdCoefficient = new Dictionary<int, double>();
|
|
foreach (var entry in nodeIdAllCoefficients)
|
|
{
|
|
coefficient = 0;
|
|
foreach (var value in entry.Value) coefficient += value;
|
|
coefficient /= entry.Value.Count();
|
|
//
|
|
nodeIdCoefficient.Add(entry.Key, coefficient);
|
|
}
|
|
//
|
|
return nodeIdCoefficient;
|
|
}
|
|
else return null;
|
|
}
|
|
public void GetSectionThicknessAssignments(out Dictionary<int, int> elementIdSectionThicknessId)
|
|
{
|
|
// Get element section ids
|
|
Dictionary<int, int> elementIdSectionId;
|
|
GetSectionAssignments(out elementIdSectionId);
|
|
// Get thicknesses
|
|
int count = 0;
|
|
double thickness;
|
|
List<int> sectionIds;
|
|
Dictionary<double, List<int>> thicknessSectionIds = new Dictionary<double, List<int>>();
|
|
foreach (var entry in _sections)
|
|
{
|
|
thickness = GetSectionThickness(entry.Value);
|
|
//
|
|
if (thicknessSectionIds.TryGetValue(thickness, out sectionIds)) sectionIds.Add(count);
|
|
else thicknessSectionIds.Add(thickness, new List<int>() { count });
|
|
count++;
|
|
}
|
|
double[] sortedThicknesses = thicknessSectionIds.Keys.ToArray();
|
|
Array.Sort(sortedThicknesses);
|
|
// Get a map of section thicknesses
|
|
Dictionary<int, int> sectionIdSectionThicknessId = new Dictionary<int, int>();
|
|
sectionIdSectionThicknessId.Add(-1, -1);
|
|
if (sortedThicknesses.Length > 0 && sortedThicknesses[0] == -1) count = -1;
|
|
else count = 0;
|
|
foreach (var sortedThickness in sortedThicknesses)
|
|
{
|
|
foreach (var sectionId in thicknessSectionIds[sortedThickness])
|
|
{
|
|
sectionIdSectionThicknessId.Add(sectionId, count);
|
|
}
|
|
count++;
|
|
}
|
|
// Use the map
|
|
elementIdSectionThicknessId = new Dictionary<int, int>();
|
|
foreach (var entry in elementIdSectionId)
|
|
elementIdSectionThicknessId.Add(entry.Key, sectionIdSectionThicknessId[entry.Value]);
|
|
}
|
|
public double GetSectionThickness(Section section)
|
|
{
|
|
if (_properties.ModelSpace.IsTwoD() && section is SolidSection sos) return sos.Thickness.Value;
|
|
else if (section is ShellSection shs) return shs.Thickness.Value;
|
|
else if (section is MembraneSection ms) return ms.Thickness.Value;
|
|
else return -1;
|
|
}
|
|
//
|
|
public void RemoveLostUserKeywords(Action<int> SetNumberOfUserKeywords)
|
|
{
|
|
try
|
|
{
|
|
FileInOut.Output.CalculixFileWriter.RemoveLostUserKeywords(this);
|
|
SetNumberOfUserKeywords?.Invoke(_calculixUserKeywords.Count);
|
|
}
|
|
catch { }
|
|
}
|
|
// Import
|
|
public string[] ImportGeometryFromStlFile(string fileName)
|
|
{
|
|
FeMesh mesh = StlFileReader.Read(fileName);
|
|
// Shading
|
|
foreach (var entry in mesh.Parts) entry.Value.SmoothShaded = true;
|
|
//
|
|
string[] addedPartNames = ImportGeometry(mesh, GetReservedPartNames());
|
|
//
|
|
return addedPartNames;
|
|
}
|
|
public string[] ImportGeometryFromBrepFile(string visFileName, string brepFileName)
|
|
{
|
|
FeMesh mesh = VisFileReader.Read(visFileName);
|
|
//
|
|
if (mesh.Parts.GetValueByIndex(0) is GeometryPart gp)
|
|
{
|
|
gp.CADFileDataFromFile(brepFileName);
|
|
gp.SmoothShaded = true;
|
|
}
|
|
//
|
|
string[] addedPartNames = ImportGeometry(mesh, GetReservedPartNames());
|
|
//
|
|
return addedPartNames;
|
|
}
|
|
public void ImportMeshFromVolFile(string fileName)
|
|
{
|
|
FeMesh mesh = VolFileReader.Read(fileName, ElementsToImport.Shell | ElementsToImport.Solid);
|
|
//
|
|
ImportMesh(mesh, GetReservedPartNames(), true, false);
|
|
}
|
|
public bool ImportMeshFromMmgFile(string fileName,
|
|
ElementsToImport elementsToImport = ElementsToImport.All,
|
|
bool convertToSecondOrder = false)
|
|
{
|
|
FeMesh mesh = MmgFileReader.Read3D(fileName, elementsToImport, MeshRepresentation.Mesh, convertToSecondOrder);
|
|
//
|
|
bool noErrors = true;
|
|
foreach (var entry in mesh.Parts)
|
|
{
|
|
if (entry.Value is GeometryPart gp && gp.HasErrors)
|
|
{
|
|
noErrors = false;
|
|
break;
|
|
}
|
|
}
|
|
//
|
|
ImportMesh(mesh, GetReservedPartNames(), true, false);
|
|
//
|
|
return noErrors;
|
|
}
|
|
|
|
public void ImportGeneratedMeshFromMeshFile(string fileName, BasePart part, bool convertToSecondOrder,
|
|
bool splitCompoundMesh, bool mergeCompoundParts)
|
|
{
|
|
ElementsToImport elementsToImport;
|
|
if (part.PartType == PartType.SolidAsShell)
|
|
{
|
|
elementsToImport = ElementsToImport.Solid;
|
|
}
|
|
else if (part.PartType == PartType.Shell)
|
|
{
|
|
elementsToImport = ElementsToImport.Shell;
|
|
}
|
|
else if (part.PartType == PartType.Compound)
|
|
{
|
|
var subPart = _geometry.Parts[((CompoundGeometryPart)part).SubPartNames[0]] as GeometryPart;
|
|
|
|
if (subPart != null && subPart.PartType == PartType.SolidAsShell)
|
|
{
|
|
elementsToImport = ElementsToImport.Solid;
|
|
}
|
|
else if (subPart != null && subPart.PartType == PartType.Shell)
|
|
{
|
|
elementsToImport = ElementsToImport.Shell;
|
|
}
|
|
else
|
|
{
|
|
throw new NotSupportedException();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw new NotSupportedException();
|
|
}
|
|
|
|
// Get part names - determine, if one part or multiple parts are needed
|
|
string[] prevPartNames;
|
|
if (part is CompoundGeometryPart cgp)
|
|
{
|
|
if (mergeCompoundParts)
|
|
{
|
|
prevPartNames = new string[] { part.Name };
|
|
}
|
|
else
|
|
{
|
|
prevPartNames = cgp.SubPartNames.ToArray();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
prevPartNames = new string[] { part.Name };
|
|
}
|
|
|
|
// Called after meshing in PrePoMax - the parts are sorted by id
|
|
FeMesh mesh;
|
|
if (Path.GetExtension(fileName) == ".vol")
|
|
{
|
|
mesh = VolFileReader.Read(fileName, elementsToImport, convertToSecondOrder);
|
|
}
|
|
else if (Path.GetExtension(fileName) == ".mesh")
|
|
{
|
|
mesh = MmgFileReader.Read(fileName, elementsToImport, MeshRepresentation.Mesh, convertToSecondOrder);
|
|
}
|
|
else if (Path.GetExtension(fileName) == ".inp")
|
|
{
|
|
mesh = InpFileReader.ReadMesh(fileName, elementsToImport, convertToSecondOrder);
|
|
}
|
|
else
|
|
{
|
|
throw new NotSupportedException();
|
|
}
|
|
|
|
// Split compound mesh
|
|
if (splitCompoundMesh)
|
|
{
|
|
mesh.SplitSolidCompoundMesh();
|
|
}
|
|
|
|
// Merge parts if only one part is expected
|
|
string[] partNames = mesh.Parts.Keys.ToArray();
|
|
if (prevPartNames.Length != partNames.Length)
|
|
{
|
|
if (prevPartNames.Length == 1 && partNames.Length > 1)
|
|
{
|
|
mesh.MergeMeshParts(mesh.Parts.Keys.ToArray(), out _, out _);
|
|
partNames = mesh.Parts.Keys.ToArray();
|
|
}
|
|
else
|
|
{
|
|
throw new NotSupportedException();
|
|
}
|
|
}
|
|
|
|
// Rename the imported part/s
|
|
BasePart[] importedParts = new BasePart[partNames.Length];
|
|
for (int i = 0; i < partNames.Length; i++)
|
|
{
|
|
importedParts[i] = mesh.Parts[partNames[i]];
|
|
importedParts[i].Name = prevPartNames[i];
|
|
}
|
|
mesh.Parts.Clear();
|
|
for (int i = 0; i < partNames.Length; i++)
|
|
{
|
|
mesh.Parts.Add(importedParts[i].Name, importedParts[i]);
|
|
}
|
|
|
|
//
|
|
ImportMesh(mesh, null, false);
|
|
|
|
// Recolor parts at the end of the import
|
|
foreach (var importedPart in importedParts)
|
|
{
|
|
if (_geometry.Parts.ContainsKey(importedPart.Name) && _mesh.Parts.ContainsKey(importedPart.Name))
|
|
{
|
|
_mesh.Parts[importedPart.Name].Color = _geometry.Parts[importedPart.Name].Color;
|
|
}
|
|
}
|
|
}
|
|
|
|
public static bool DeepCompare(object obj, object another)
|
|
{
|
|
if (ReferenceEquals(obj, another))
|
|
return true;
|
|
if ((obj == null) || (another == null))
|
|
return false;
|
|
//Compare two object's class, return false if they are difference
|
|
if (obj.GetType() != another.GetType())
|
|
return false;
|
|
|
|
var result = true;
|
|
//Get all properties of obj
|
|
//And compare each other
|
|
foreach (var property in obj.GetType().GetProperties())
|
|
{
|
|
var objValue = property.GetValue(obj);
|
|
var anotherValue = property.GetValue(another);
|
|
if (!objValue.Equals(anotherValue))
|
|
result = false;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public static bool CompareEx(object obj, object another)
|
|
{
|
|
if (ReferenceEquals(obj, another))
|
|
return true;
|
|
if ((obj == null) || (another == null))
|
|
return false;
|
|
if (obj.GetType() != another.GetType())
|
|
return false;
|
|
|
|
//properties: int, double, DateTime, etc, not class
|
|
if (!obj.GetType().IsClass) return obj.Equals(another);
|
|
|
|
var result = true;
|
|
foreach (var property in obj.GetType().GetProperties())
|
|
{
|
|
var objValue = property.GetValue(obj);
|
|
var anotherValue = property.GetValue(another);
|
|
//Recursion
|
|
if (!DeepCompare(objValue, anotherValue))
|
|
result = false;
|
|
}
|
|
return result;
|
|
}
|
|
public void ImportGeneratedRemeshFromMeshFile(string fileName, int[] elementIds, BasePart part,
|
|
bool convertToSecondOrder, Dictionary<int[], FeNode> midNodes)
|
|
{
|
|
// Remove elements from the mesh
|
|
HashSet<int> possiblyUnrefNodeIds = new HashSet<int>();
|
|
foreach (var elId in elementIds) possiblyUnrefNodeIds.UnionWith(_mesh.Elements[elId].NodeIds); // contains midside nodes
|
|
HashSet<int> removedNodeIds =
|
|
_mesh.RemoveElementsByIds(new HashSet<int>(elementIds), possiblyUnrefNodeIds, false, false, true);
|
|
HashSet<int> borderNodeIds = new HashSet<int>(possiblyUnrefNodeIds.Except(removedNodeIds)); // contains midside nodes
|
|
Dictionary<int, FeNode> borderNodes = new Dictionary<int, FeNode>();
|
|
foreach (var ndId in borderNodeIds) borderNodes.Add(ndId, _mesh.Nodes[ndId]); // contains midside nodes
|
|
HashSet<int> remainingNodeIds = new HashSet<int>(_mesh.Nodes.Keys.Except(removedNodeIds));
|
|
// Read the mmg file and renumber nodes and elements
|
|
double epsilon = 1E-6;
|
|
double max = part.BoundingBox.GetDiagonal();
|
|
Dictionary<string, Dictionary<int, int>> partNameNewSurfIdOldSurfId = new Dictionary<string, Dictionary<int, int>>();
|
|
Dictionary<string, Dictionary<int, int>> partNameNewEdgeIdOldEdgeId = new Dictionary<string, Dictionary<int, int>>();
|
|
FeMesh mmgMesh = MmgFileReader.Read(fileName, ElementsToImport.Shell,
|
|
MeshRepresentation.Mesh, convertToSecondOrder,
|
|
_mesh.MaxNodeId + 1, _mesh.MaxElementId + 1,
|
|
borderNodes, midNodes,
|
|
epsilon * max,
|
|
partNameNewSurfIdOldSurfId, partNameNewEdgeIdOldEdgeId);
|
|
// Get surface nodes before modification
|
|
Dictionary<int, HashSet<int>> surfaceIdNodeIds = part.Visualization.GetSurfaceIdNodeIds();
|
|
foreach (var entry in surfaceIdNodeIds) entry.Value.ExceptWith(removedNodeIds);
|
|
// Get edge nodes before modification
|
|
Dictionary<int, HashSet<int>> edgeIdNodeIds = part.Visualization.GetEdgeIdNodeIds();
|
|
foreach (var entry in edgeIdNodeIds) entry.Value.ExceptWith(removedNodeIds);
|
|
// Add elements to mesh
|
|
FeElement element;
|
|
HashSet<Type> elementTypes = new HashSet<Type>();
|
|
foreach (var entry in mmgMesh.Elements)
|
|
{
|
|
element = entry.Value;
|
|
element.PartId = part.PartId;
|
|
elementTypes.Add(element.GetType());
|
|
_mesh.Elements.Add(entry.Key, element);
|
|
}
|
|
// Add elements to part
|
|
HashSet<int> newPartElementIds = new HashSet<int>(part.Labels);
|
|
newPartElementIds.UnionWith(mmgMesh.Elements.Keys);
|
|
part.Labels = newPartElementIds.ToArray();
|
|
Array.Sort(part.Labels);
|
|
// Add element types to part
|
|
if (part is MeshPart mp) mp.AddElementTypes(elementTypes.ToArray());
|
|
// Add nodes to mesh
|
|
foreach (var entry in mmgMesh.Nodes)
|
|
{
|
|
if (!_mesh.Nodes.ContainsKey(entry.Key)) _mesh.Nodes.Add(entry.Key, entry.Value);
|
|
}
|
|
// Add nodes to part
|
|
HashSet<int> newPartNodeIds = new HashSet<int>(part.NodeLabels);
|
|
newPartNodeIds.UnionWith(mmgMesh.Nodes.Keys);
|
|
part.NodeLabels = newPartNodeIds.ToArray();
|
|
Array.Sort(part.NodeLabels);
|
|
// Update node ids
|
|
_mesh.UpdateMaxNodeAndElementIds();
|
|
_mesh.UpdateNodeIdElementIds();
|
|
// Model vertices
|
|
HashSet<int> vertexNodeIds = new HashSet<int>();
|
|
// Get vertices from part - only those that were not removed
|
|
foreach (var nodeId in part.Visualization.VertexNodeIds)
|
|
{
|
|
if (!removedNodeIds.Contains(nodeId)) vertexNodeIds.Add(nodeId);
|
|
}
|
|
// Get vertices from mmgPart
|
|
foreach (var entry in mmgMesh.Parts) vertexNodeIds.UnionWith(entry.Value.Visualization.VertexNodeIds);
|
|
// Model edges
|
|
int elementId = mmgMesh.MaxElementId + 1;
|
|
LinearBeamElement beamElement;
|
|
List<FeElement1D> edgeElements = new List<FeElement1D>();
|
|
// Get model edges from part - only edges that are not completely removed
|
|
int[] key;
|
|
CompareIntArray comparer = new CompareIntArray();
|
|
HashSet<int[]> edgeKeys = new HashSet<int[]>(comparer);
|
|
//
|
|
foreach (var edgeCell in part.Visualization.EdgeCells)
|
|
{
|
|
if (remainingNodeIds.Contains(edgeCell[0]) && remainingNodeIds.Contains(edgeCell[1]))
|
|
{
|
|
key = Tools.GetSortedKey(edgeCell[0], edgeCell[1]);
|
|
if (!edgeKeys.Contains(key))
|
|
{
|
|
beamElement = new LinearBeamElement(elementId++, key);
|
|
edgeElements.Add(beamElement);
|
|
edgeKeys.Add(key);
|
|
}
|
|
}
|
|
}
|
|
// Get model edges from mmgPart
|
|
foreach (var entry in mmgMesh.Parts)
|
|
{
|
|
foreach (var edgeCell in entry.Value.Visualization.EdgeCells)
|
|
{
|
|
key = Tools.GetSortedKey(edgeCell[0], edgeCell[1]);
|
|
if (!edgeKeys.Contains(key))
|
|
{
|
|
beamElement = new LinearBeamElement(elementId++, key);
|
|
edgeElements.Add(beamElement);
|
|
edgeKeys.Add(key);
|
|
}
|
|
}
|
|
}
|
|
// Add edge elements to mesh
|
|
foreach (var edgeElement in edgeElements) _mesh.Elements.Add(edgeElement.Id, edgeElement);
|
|
// Extract visualization
|
|
_mesh.ExtractShellPartVisualization(part, false, -1);
|
|
//
|
|
_mesh.ConvertLineFeElementsToEdges(vertexNodeIds, false, part.Name);
|
|
// Renumber surfaces
|
|
BasePart mmgPart;
|
|
Dictionary<int, HashSet<int>> itemIdNodeIds;
|
|
foreach (var partNewSurfIdOldSurfId in partNameNewSurfIdOldSurfId)
|
|
{
|
|
mmgPart = mmgMesh.Parts[partNewSurfIdOldSurfId.Key];
|
|
itemIdNodeIds = mmgPart.Visualization.GetSurfaceIdNodeIds();
|
|
//
|
|
foreach (var newSurfIdOldSurfId in partNewSurfIdOldSurfId.Value)
|
|
{
|
|
surfaceIdNodeIds[newSurfIdOldSurfId.Value].UnionWith(itemIdNodeIds[newSurfIdOldSurfId.Key]);
|
|
surfaceIdNodeIds[newSurfIdOldSurfId.Value].IntersectWith(part.NodeLabels);
|
|
}
|
|
}
|
|
_mesh.RenumberVisualizationSurfaces(part, surfaceIdNodeIds);
|
|
// Renumber edges
|
|
foreach (var partNewEdgeIdOldEdgeId in partNameNewEdgeIdOldEdgeId)
|
|
{
|
|
if (mmgMesh.Parts.TryGetValue(partNewEdgeIdOldEdgeId.Key, out mmgPart))
|
|
{
|
|
itemIdNodeIds = mmgPart.Visualization.GetEdgeIdNodeIds();
|
|
//
|
|
foreach (var newEdgeIdOldEdgeId in partNewEdgeIdOldEdgeId.Value)
|
|
{
|
|
edgeIdNodeIds[newEdgeIdOldEdgeId.Value].UnionWith(itemIdNodeIds[newEdgeIdOldEdgeId.Key]);
|
|
edgeIdNodeIds[newEdgeIdOldEdgeId.Value].IntersectWith(part.NodeLabels);
|
|
}
|
|
}
|
|
}
|
|
_mesh.RenumberPartVisualizationEdges(part, edgeIdNodeIds);
|
|
//
|
|
_mesh.RemoveElementsByType<FeElement1D>();
|
|
//
|
|
_mesh.ComputeVolumeArea(part);
|
|
//
|
|
UpdateMeshPartsElementTypes(false);
|
|
}
|
|
public List<string> ImportModelFromInpFile(string fileName, Action<string> WriteDataToOutput)
|
|
{
|
|
OrderedDictionary<int[], Calculix.CalculixUserKeyword> indexedUserKeywords;
|
|
InpFileReader.Read(fileName,
|
|
ElementsToImport.Solid | ElementsToImport.Shell,
|
|
this,
|
|
WriteDataToOutput,
|
|
out indexedUserKeywords);
|
|
//
|
|
CalculixUserKeywords = indexedUserKeywords;
|
|
//
|
|
return InpFileReader.Errors;
|
|
}
|
|
public List<string> ImportMaterialsFromInpFile(string fileName, Action<string> WriteDataToOutput)
|
|
{
|
|
FeModel model = new FeModel("Imported Materials", _unitSystem);
|
|
model.Properties.ModelSpace = _properties.ModelSpace;
|
|
// Add existing model materials to account for indexing of material user keywords
|
|
string existingMaterialName;
|
|
foreach (var entry in _materials)
|
|
{
|
|
existingMaterialName = "_internal_for_indices_" + entry.Key;
|
|
model.Materials.Add(existingMaterialName, entry.Value.DeepClone());
|
|
}
|
|
List<string> existingMaterialNames = model.Materials.Keys.ToList();
|
|
//
|
|
InpFileReader.Read(fileName,
|
|
ElementsToImport.Solid | ElementsToImport.Shell,
|
|
model,
|
|
WriteDataToOutput,
|
|
out OrderedDictionary<int[], Calculix.CalculixUserKeyword> indexedUserKeywords);
|
|
//
|
|
CalculixUserKeywords = indexedUserKeywords;
|
|
// Remove existing model materials
|
|
foreach (var materialName in existingMaterialNames) model.Materials.Remove(materialName);
|
|
//
|
|
string name;
|
|
foreach (var entry in model.Materials)
|
|
{
|
|
name = entry.Key;
|
|
if (_materials.ContainsKey(name))
|
|
{
|
|
name += "_Imported";
|
|
if (_materials.ContainsKey(name))
|
|
{
|
|
name = _materials.GetNextNumberedKey(name);
|
|
}
|
|
}
|
|
entry.Value.Name = name;
|
|
_materials.Add(name, entry.Value);
|
|
}
|
|
//
|
|
return InpFileReader.Errors;
|
|
}
|
|
public void ImportMeshFromUnvFile(string fileName)
|
|
{
|
|
FeMesh mesh = UnvFileReader.Read(fileName, ElementsToImport.Shell | ElementsToImport.Solid);
|
|
//
|
|
ImportMesh(mesh, GetReservedPartNames(), true, false);
|
|
}
|
|
public void ImportMeshFromObjFile(string fileName)
|
|
{
|
|
FeMesh mesh = ObjFileReader.Read(fileName);
|
|
//
|
|
ImportMesh(mesh, GetReservedPartNames(), true, false);
|
|
}
|
|
//
|
|
private string[] ImportGeometry(FeMesh mesh, ICollection<string> reservedPartNames)
|
|
{
|
|
Check2D(mesh);
|
|
//
|
|
if (_geometry == null)
|
|
{
|
|
_geometry = new FeMesh(MeshRepresentation.Geometry);
|
|
mesh.ResetPartsColor();
|
|
}
|
|
//
|
|
string[] addedPartNames = _geometry.AddMesh(mesh, reservedPartNames, GetReservedPartIds());
|
|
return addedPartNames;
|
|
}
|
|
public void ImportMesh(FeMesh mesh, ICollection<string> reservedPartNames, bool forceRenameParts = true,
|
|
bool renumberNodesAndElements = true)
|
|
{
|
|
Check2D(mesh);
|
|
//
|
|
if (_mesh == null)
|
|
{
|
|
_mesh = new FeMesh(MeshRepresentation.Mesh);
|
|
mesh.ResetPartsColor();
|
|
}
|
|
_mesh.AddMesh(mesh, reservedPartNames, GetReservedPartIds(), forceRenameParts, renumberNodesAndElements);
|
|
}
|
|
// Update
|
|
public void UpdateGeometryPartMassPropertiesFromVisFile(string visFileName, string partName)
|
|
{
|
|
FeMesh mesh = VisFileReader.Read(visFileName);
|
|
//
|
|
if (mesh.Parts.Count == 1)
|
|
{
|
|
BasePart part = _geometry.Parts[partName];
|
|
BasePart updatedPart = mesh.Parts.First().Value;
|
|
//part.Visualization = updatedPart.Visualization;
|
|
part.MassProperties = updatedPart.MassProperties;
|
|
}
|
|
}
|
|
// Setters
|
|
public void SetMesh(FeMesh mesh)
|
|
{
|
|
_mesh = mesh;
|
|
}
|
|
// Getters
|
|
public string[] GetAllMeshEntityNames()
|
|
{
|
|
if (_mesh != null) return _mesh.GetAllMeshEntityNames();
|
|
return new string[0];
|
|
}
|
|
public HashSet<string> GetReservedPartNames()
|
|
{
|
|
HashSet<string> reservedPartNames = new HashSet<string>();
|
|
if (_geometry != null && _geometry.Parts != null) reservedPartNames.UnionWith(_geometry.Parts.Keys);
|
|
reservedPartNames.UnionWith(GetAllMeshEntityNames());
|
|
return reservedPartNames;
|
|
}
|
|
public HashSet<int> GetReservedPartIds()
|
|
{
|
|
HashSet<int> reservedPartIds = new HashSet<int>();
|
|
if (_geometry != null && _geometry.Parts != null) reservedPartIds.UnionWith(_geometry.GetAllPartIds());
|
|
if (_mesh != null && _mesh.Parts != null) reservedPartIds.UnionWith(_mesh.GetAllPartIds());
|
|
return reservedPartIds;
|
|
}
|
|
// Mesh setup items
|
|
public string IsMeshSetupItemProperlyDefined(MeshSetupItem meshSetupItem)
|
|
{
|
|
if (meshSetupItem is MeshingParameters) return null;
|
|
else if (meshSetupItem is FeMeshRefinement) return null;
|
|
else if (meshSetupItem is ShellGmsh sg) return IsShellGmshProperlyDefined(sg);
|
|
else if (meshSetupItem is ThickenShellMesh tsm) return IsThickenShellMeshProperlyDefined(tsm);
|
|
else if (meshSetupItem is TetrahedralGmsh tg) return IsTetrahedralGmshProperlyDefined(tg);
|
|
else if (meshSetupItem is TransfiniteMesh tm) return IsTransfiniteMeshProperlyDefined(tm);
|
|
else if (meshSetupItem is ExtrudeMesh em) return IsExtrudeMeshProperlyDefined(em);
|
|
else if (meshSetupItem is SweepMesh sm) return IsSweepMeshProperlyDefined(sm);
|
|
else if (meshSetupItem is RevolveMesh rm) return IsRevolveMeshProperlyDefined(rm);
|
|
else throw new NotSupportedException("MeshSetupItemTypeException");
|
|
}
|
|
private string IsShellGmshProperlyDefined(ShellGmsh shellGmsh)
|
|
{
|
|
BasePart part;
|
|
int[] partIds = FeMesh.GetPartIdsFromGeometryIds(shellGmsh.CreationIds);
|
|
//
|
|
foreach (var partId in partIds)
|
|
{
|
|
part = _geometry.GetPartFromId(partId);
|
|
//
|
|
if (part.PartType != PartType.Shell)
|
|
return "The shell gmsh setup item can only be defined on shell parts.";
|
|
}
|
|
return null;
|
|
}
|
|
private string IsThickenShellMeshProperlyDefined(ThickenShellMesh thickenShellMesh)
|
|
{
|
|
if (thickenShellMesh.PartNames != null && thickenShellMesh.PartNames.Length > 0)
|
|
{
|
|
BasePart part;
|
|
foreach (var partName in thickenShellMesh.PartNames)
|
|
{
|
|
_geometry.Parts.TryGetValue(partName, out part);
|
|
// Is mesh setup item defined on the mesh part
|
|
if (part == null) part = _mesh.Parts[partName];
|
|
if (part == null)
|
|
return "The part " + partName + " cannot be found neither in geometry parts neither in model parts.";
|
|
//
|
|
if (part.PartType != PartType.Shell)
|
|
return "The thicken shell feature can only be used on shell parts.";
|
|
if (part.Visualization.IsNonManifold())
|
|
return "The thicken shell feature can only be used on manifold shell parts.";
|
|
}
|
|
}
|
|
else return "No parts are defined for the thicken shell mesh setup item.";
|
|
//
|
|
return null;
|
|
}
|
|
private string IsTetrahedralGmshProperlyDefined(TetrahedralGmsh tetrahedralGmsh)
|
|
{
|
|
BasePart part;
|
|
int[] partIds = FeMesh.GetPartIdsFromGeometryIds(tetrahedralGmsh.CreationIds);
|
|
//
|
|
foreach (var partId in partIds)
|
|
{
|
|
part = _geometry.GetPartFromId(partId);
|
|
//
|
|
if (part.PartType != PartType.Solid && part.PartType != PartType.SolidAsShell)
|
|
return "The tetrahedral gmsh setup item can only be defined on solid parts.";
|
|
}
|
|
return null;
|
|
}
|
|
private string IsTransfiniteMeshProperlyDefined(TransfiniteMesh transfiniteMesh)
|
|
{
|
|
GeometryPart part;
|
|
int[] partIds = FeMesh.GetPartIdsFromGeometryIds(transfiniteMesh.CreationIds);
|
|
//
|
|
int num3sided;
|
|
int num4sided;
|
|
HashSet<int> triSurfaceEdgeIds = new HashSet<int>();
|
|
foreach (var partId in partIds)
|
|
{
|
|
part = (GeometryPart)_geometry.GetPartFromId(partId);
|
|
//
|
|
if (!part.IsCADPart)
|
|
return "The transfinite gmsh setup item cannot be defined on a stl based part.";
|
|
if (part.PartType != PartType.Solid && part.PartType != PartType.SolidAsShell)
|
|
return "The transfinite gmsh setup item can only be defined on solid parts.";
|
|
//
|
|
//if (System.Diagnostics.Debugger.IsAttached || transfiniteMesh.AllowPrismElements) return null;
|
|
if (transfiniteMesh.AllowPyramidElements) return null;
|
|
else
|
|
{
|
|
if (part.Visualization.FaceCount == 5 || part.Visualization.FaceCount == 6)
|
|
{
|
|
num3sided = 0;
|
|
num4sided = 0;
|
|
triSurfaceEdgeIds.Clear();
|
|
for (int i = 0; i < part.Visualization.FaceCount; i++)
|
|
{
|
|
if (part.Visualization.FaceEdgeIds[i].Length == 3)
|
|
{
|
|
num3sided++;
|
|
triSurfaceEdgeIds.UnionWith(part.Visualization.FaceEdgeIds[i]);
|
|
}
|
|
else if (part.Visualization.FaceEdgeIds[i].Length == 4) num4sided++;
|
|
}
|
|
if (!(num4sided == 6 || (num4sided == 3 && num3sided == 2)))
|
|
return "The transfinite gmsh setup item can only be defined on solid parts with 3-sided and " +
|
|
"4-sided faces.";
|
|
if (num4sided == 3 && num3sided == 2 && triSurfaceEdgeIds.Count != 6)
|
|
return "The transfinite gmsh setup item can only be defined on solid parts with non touching " +
|
|
"3-sided faces.";
|
|
}
|
|
else return "The transfinite gmsh setup item can only be defined on solid parts with 5 or 6 faces.";
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
private string IsExtrudeMeshProperlyDefined(ExtrudeMesh extrudeMesh)
|
|
{
|
|
string error = null;
|
|
extrudeMesh.Direction = null;
|
|
extrudeMesh.ExtrudeCenter = null;
|
|
//
|
|
if (extrudeMesh.AlgorithmMesh2D == CaeMesh.Meshing.GmshAlgorithmMesh2DEnum.QuasiStructuredQuad)
|
|
return "The extrude mesh setup item cannot use the quasi-structured quad algorithm.";
|
|
//
|
|
if (extrudeMesh.ElementSizeType == CaeMesh.Meshing.ElementSizeTypeEnum.MultiLayerd &&
|
|
(extrudeMesh.LayerSizes.Length != extrudeMesh.NumOfElementsPerLayer.Length))
|
|
return "The number of layers must be equal for layer sizes and number of elements per layer.";
|
|
//
|
|
int[] selectedPartIds = FeMesh.GetPartIdsFromGeometryIds(extrudeMesh.CreationIds);
|
|
//
|
|
if (selectedPartIds.Length == 1)
|
|
{
|
|
// Get part
|
|
int partId = selectedPartIds[0];
|
|
GeometryPart part = (GeometryPart)_geometry.GetPartFromId(partId);
|
|
//
|
|
if (IsPartCompoundSubPart(part.Name))
|
|
return "The extrude mesh setup item cannot be defined on a compound part.";
|
|
if (!part.IsCADPart)
|
|
return "The extrude mesh setup item cannot be defined on a stl based part.";
|
|
if (part.PartType != PartType.Solid && part.PartType != PartType.SolidAsShell)
|
|
return "The extrude mesh setup item cannot be defined on a shell part.";
|
|
//
|
|
VisualizationData vis = part.Visualization;
|
|
// Get surface ids
|
|
HashSet<int> surfaceIds = new HashSet<int>();
|
|
for (int i = 0; i < extrudeMesh.CreationIds.Length; i++)
|
|
surfaceIds.Add(FeMesh.GetItemIdFromGeometryId(extrudeMesh.CreationIds[i]));
|
|
// Do selected surfaces form a connected surface
|
|
if (vis.AreSurfacesConnected(surfaceIds.ToArray()))
|
|
{
|
|
Dictionary<int, HashSet<int>> vertexIdEdgeId = vis.GetVertexIdEdgeIds();
|
|
HashSet<int> surfaceVertices = vis.GetVertexNodeIdsForSurfaceIds(surfaceIds.ToArray());
|
|
HashSet<int> surfaceEdgeIds = vis.GetEdgeIdsForSurfaceIds(surfaceIds.ToArray());
|
|
//
|
|
HashSet<int> directionEdgeIds = new HashSet<int>();
|
|
foreach (var vertexId in surfaceVertices)
|
|
directionEdgeIds.UnionWith(vertexIdEdgeId[vertexId].Except(surfaceEdgeIds));
|
|
//
|
|
double directionLength = vis.GetEqualEdgeLengthOrNegative(directionEdgeIds);
|
|
if (directionLength > 0)
|
|
{
|
|
bool straightEdges = false;
|
|
if (vis.EdgeTypes != null)
|
|
{
|
|
HashSet<GeomCurveType> edgeTypes = new HashSet<GeomCurveType>();
|
|
foreach (var directionEdgeId in directionEdgeIds) edgeTypes.Add(vis.EdgeTypes[directionEdgeId]);
|
|
//
|
|
if (edgeTypes.Count() == 1 && edgeTypes.First() == GeomCurveType.Line) straightEdges = true;
|
|
}
|
|
if (!straightEdges)
|
|
{
|
|
straightEdges = true;
|
|
double curvature;
|
|
foreach (var directionEdgeId in directionEdgeIds)
|
|
{
|
|
curvature = vis.GetMaxEdgeCurvature(directionEdgeId, _geometry.Nodes);
|
|
if (curvature > 0.001)
|
|
{
|
|
straightEdges = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (straightEdges)
|
|
{
|
|
int[] edgeNodes;
|
|
HashSet<int> nodeIds;
|
|
Vec3D normalizedDirection = null;
|
|
BoundingBox bb = new BoundingBox();
|
|
foreach (var directionEdgeId in directionEdgeIds)
|
|
{
|
|
// Get all edge node ids
|
|
nodeIds = vis.GetNodeIdsForEdgeId(directionEdgeId);
|
|
// Reduce edge node ids to vertices
|
|
edgeNodes = nodeIds.Intersect(vis.VertexNodeIds).ToArray();
|
|
if (edgeNodes.Length != 2) throw new NotSupportedException();
|
|
// First node must be on the selected surface
|
|
if (!surfaceVertices.Contains(edgeNodes[0]))
|
|
(edgeNodes[0], edgeNodes[1]) = (edgeNodes[1], edgeNodes[0]);
|
|
// Compute the extrude direction
|
|
normalizedDirection = _geometry.ComputeDirectionFromEdgeCellIndices(edgeNodes, edgeNodes[0]);
|
|
bb.IncludeCoor(normalizedDirection.Coor);
|
|
}
|
|
//
|
|
if (normalizedDirection != null && bb.GetDiagonal() < 0.0017) // approximately 0.1° difference
|
|
{
|
|
extrudeMesh.Direction = (directionLength * normalizedDirection).Coor;
|
|
//
|
|
bb = _geometry.GetBoundingBoxForNodeIds(surfaceVertices.ToArray());
|
|
extrudeMesh.ExtrudeCenter = bb.GetCenter();
|
|
//
|
|
// Round
|
|
double d = part.BoundingBox.GetDiagonal();
|
|
int digits = (int)Math.Log10(d) - 6;
|
|
if (digits > 0) digits = 0;
|
|
else if (digits < 0) digits *= -1;
|
|
//
|
|
extrudeMesh.Direction[0] = Math.Round(extrudeMesh.Direction[0], digits);
|
|
extrudeMesh.Direction[1] = Math.Round(extrudeMesh.Direction[1], digits);
|
|
extrudeMesh.Direction[2] = Math.Round(extrudeMesh.Direction[2], digits);
|
|
//
|
|
extrudeMesh.ExtrudeCenter[0] = Math.Round(extrudeMesh.ExtrudeCenter[0], digits);
|
|
extrudeMesh.ExtrudeCenter[1] = Math.Round(extrudeMesh.ExtrudeCenter[1], digits);
|
|
extrudeMesh.ExtrudeCenter[2] = Math.Round(extrudeMesh.ExtrudeCenter[2], digits);
|
|
//
|
|
}
|
|
else error = "The selected surfaces have direction edges pointing in different direction.";
|
|
}
|
|
else error = "The selected surfaces have a direction edge that is not a straight line.";
|
|
}
|
|
else error = "The selected surfaces have direction edges of different lengths.";
|
|
}
|
|
else error = "The selected surfaces are not connected.";
|
|
}
|
|
else error = "The surfaces of more than one part are selected.";
|
|
//
|
|
return error == null ? null : error + " The extruded mesh cannot be created.";
|
|
}
|
|
private string IsSweepMeshProperlyDefined(SweepMesh sweepMesh)
|
|
{
|
|
string error = null;
|
|
sweepMesh.Direction = null;
|
|
sweepMesh.SweepCenter = null;
|
|
//
|
|
if (sweepMesh.ElementSizeType == CaeMesh.Meshing.ElementSizeTypeEnum.MultiLayerd &&
|
|
(sweepMesh.LayerSizes.Length != sweepMesh.NumOfElementsPerLayer.Length))
|
|
return "The number of layers must be equal for layer sizes and number of elements per layer.";
|
|
//
|
|
int[] selectedPartIds = FeMesh.GetPartIdsFromGeometryIds(sweepMesh.CreationIds);
|
|
//
|
|
if (selectedPartIds.Length == 1)
|
|
{
|
|
// Get part
|
|
int partId = selectedPartIds[0];
|
|
GeometryPart part = (GeometryPart)_geometry.GetPartFromId(partId);
|
|
//
|
|
if (IsPartCompoundSubPart(part.Name))
|
|
return "The sweep mesh setup item cannot be defined on a compound part.";
|
|
if (!part.IsCADPart)
|
|
return "The sweep mesh setup item cannot be defined on a stl based part.";
|
|
if (part.PartType != PartType.Solid && part.PartType != PartType.SolidAsShell)
|
|
return "The sweep mesh setup item cannot be defined on a shell part.";
|
|
//
|
|
VisualizationData vis = part.Visualization;
|
|
// Get surface ids
|
|
HashSet<int> sourceSurfaceIds = new HashSet<int>();
|
|
for (int i = 0; i < sweepMesh.CreationIds.Length; i++)
|
|
sourceSurfaceIds.Add(FeMesh.GetItemIdFromGeometryId(sweepMesh.CreationIds[i]));
|
|
// Do selected surfaces form a connected surface
|
|
Dictionary<int, HashSet<int>> surfaceIdSurfaceNeighbourIds = vis.GetSurfaceIdSurfaceNeighbourIds();
|
|
if (vis.AreSurfacesConnected(sourceSurfaceIds.ToArray(), surfaceIdSurfaceNeighbourIds))
|
|
{
|
|
// Are there any free edge loops with a single edge - cylinder with a single seam line
|
|
int singleEdgeLoops = 0;
|
|
HashSet<int>[] freeEdgeLoops = vis.GetFreeEdgeLoops(sourceSurfaceIds.ToArray());
|
|
for (int i = 0; i < freeEdgeLoops.Length; i++)
|
|
{
|
|
if (freeEdgeLoops[i].Count == 1) singleEdgeLoops++;
|
|
}
|
|
if (singleEdgeLoops == 0)
|
|
{
|
|
bool loop = true;
|
|
HashSet<int> neighbours;
|
|
HashSet<int> neighbourSurfaceIds;
|
|
List<int[]> layerSideSurfaceIds = new List<int[]>();
|
|
HashSet<int> visitedSurfaceIds = new HashSet<int>(sourceSurfaceIds);
|
|
//
|
|
Node<int> node;
|
|
Graph<int> connections;
|
|
Dictionary<int, Node<int>> surfaceIdNode;
|
|
List<Graph<int>> surfaceLoops;
|
|
//
|
|
while (true)
|
|
{
|
|
// New neighbours
|
|
neighbourSurfaceIds = new HashSet<int>();
|
|
// Find next neighbours
|
|
foreach (var visitedSurfaceId in visitedSurfaceIds)
|
|
{
|
|
neighbourSurfaceIds.UnionWith(surfaceIdSurfaceNeighbourIds[visitedSurfaceId]);
|
|
}
|
|
neighbourSurfaceIds.ExceptWith(visitedSurfaceIds);
|
|
// Get neighbours of neighbours
|
|
neighbours = new HashSet<int>();
|
|
foreach (var id in neighbourSurfaceIds) neighbours.UnionWith(surfaceIdSurfaceNeighbourIds[id]);
|
|
neighbours.ExceptWith(visitedSurfaceIds); // remove visited
|
|
neighbours.ExceptWith(neighbourSurfaceIds); // remove self
|
|
// Are neighbours creating a closed surface compound
|
|
if (neighbours.Count == 0) loop = false;
|
|
// Check that neighbours form a loop
|
|
else
|
|
{
|
|
// Split surfaces into connected groups
|
|
connections = new Graph<int>();
|
|
surfaceIdNode = new Dictionary<int, Node<int>>();
|
|
//
|
|
foreach (var neighbourSurfaceId in neighbourSurfaceIds)
|
|
{
|
|
node = new Node<int>(neighbourSurfaceId);
|
|
connections.AddNode(node);
|
|
surfaceIdNode.Add(neighbourSurfaceId, node);
|
|
}
|
|
foreach (var neighbour1Id in neighbourSurfaceIds)
|
|
{
|
|
foreach (var neighbour2Id in surfaceIdSurfaceNeighbourIds[neighbour1Id])
|
|
{
|
|
if (neighbourSurfaceIds.Contains(neighbour2Id))
|
|
connections.AddUndirectedEdge(surfaceIdNode[neighbour1Id],
|
|
surfaceIdNode[neighbour2Id]);
|
|
}
|
|
}
|
|
surfaceLoops = connections.GetConnectedSubgraphs();
|
|
//
|
|
if (surfaceLoops.Count() != freeEdgeLoops.Count()) loop = false;
|
|
else
|
|
{
|
|
foreach (var surfaceLoop in surfaceLoops)
|
|
{
|
|
// One cylindrical face
|
|
if (surfaceLoop.Count == 1) loop = false;
|
|
// Two cylindrical faces
|
|
else if (surfaceLoop.Count == 2)
|
|
{
|
|
foreach (var id in surfaceLoop.GetValues())
|
|
{
|
|
neighbours =
|
|
surfaceIdSurfaceNeighbourIds[id].Intersect(neighbourSurfaceIds).ToHashSet();
|
|
if (neighbours.Count() != 1) { loop = false; break; }
|
|
}
|
|
}
|
|
else
|
|
{
|
|
foreach (var id in surfaceLoop.GetValues())
|
|
{
|
|
neighbours =
|
|
surfaceIdSurfaceNeighbourIds[id].Intersect(neighbourSurfaceIds).ToHashSet();
|
|
//
|
|
if (neighbours.Count() != 2) { loop = false; break; }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//
|
|
if (!loop) break;
|
|
//
|
|
layerSideSurfaceIds.Add(neighbourSurfaceIds.ToArray());
|
|
visitedSurfaceIds.UnionWith(neighbourSurfaceIds);
|
|
}
|
|
//
|
|
int[] targetSurfaceIds = surfaceIdSurfaceNeighbourIds.Keys.Except(visitedSurfaceIds).ToArray();
|
|
if (targetSurfaceIds.Length == 1)
|
|
{
|
|
int[] sideSurfaceIds = visitedSurfaceIds.Except(sourceSurfaceIds).ToArray();
|
|
//
|
|
if (sideSurfaceIds.Length > 0)
|
|
{
|
|
// Are all side surfaces 4-sided
|
|
bool fourSided = true;
|
|
foreach (var surfaceId in sideSurfaceIds)
|
|
{
|
|
if (vis.FaceEdgeIds[surfaceId].Length != 4 && !vis.IsSurfaceACylinderLike(surfaceId, out _))
|
|
{
|
|
fourSided = false;
|
|
break;
|
|
}
|
|
}
|
|
//
|
|
if (fourSided)
|
|
{
|
|
Dictionary<int, HashSet<int>> vertexIdEdgeId = vis.GetVertexIdEdgeIds();
|
|
HashSet<int> surfaceEdgeIds = vis.GetEdgeIdsForSurfaceIds(sourceSurfaceIds.ToArray());
|
|
HashSet<int> surfaceVertices = vis.GetVertexNodeIdsForSurfaceIds(sourceSurfaceIds.ToArray());
|
|
//
|
|
HashSet<int> directionEdgeIds = new HashSet<int>();
|
|
foreach (var vertexId in surfaceVertices)
|
|
directionEdgeIds.UnionWith(vertexIdEdgeId[vertexId].Except(surfaceEdgeIds));
|
|
//
|
|
int[] edgeNodes;
|
|
HashSet<int> nodeIds;
|
|
Vec3D normalizedDirection;
|
|
Vec3D averageDirection = new Vec3D();
|
|
BoundingBox bb;
|
|
int edgeCellId;
|
|
int[] edgeCell;
|
|
foreach (var directionEdgeId in directionEdgeIds)
|
|
{
|
|
// Get all edge node ids
|
|
nodeIds = vis.GetNodeIdsForEdgeId(directionEdgeId);
|
|
// Reduce edge node ids to vertices
|
|
edgeNodes = nodeIds.Intersect(vis.VertexNodeIds).ToArray();
|
|
if (edgeNodes.Length != 2) throw new NotSupportedException();
|
|
// First node must be on the selected surface
|
|
if (!surfaceVertices.Contains(edgeNodes[0]))
|
|
(edgeNodes[0], edgeNodes[1]) = (edgeNodes[1], edgeNodes[0]);
|
|
// Find the first edge cell for direction
|
|
for (int i = 0; i < vis.EdgeCellIdsByEdge[directionEdgeId].Length; i++)
|
|
{
|
|
edgeCellId = vis.EdgeCellIdsByEdge[directionEdgeId][i];
|
|
edgeCell = vis.EdgeCells[edgeCellId];
|
|
if (edgeNodes[0] == edgeCell[0])
|
|
{
|
|
edgeNodes[1] = edgeCell[1];
|
|
break;
|
|
}
|
|
else if (edgeNodes[0] == edgeCell[1])
|
|
{
|
|
edgeNodes[1] = edgeCell[0];
|
|
break;
|
|
}
|
|
}
|
|
// Compute the sweep direction
|
|
normalizedDirection =
|
|
_geometry.ComputeDirectionFromEdgeCellIndices(edgeNodes, edgeNodes[0]);
|
|
averageDirection += normalizedDirection;
|
|
}
|
|
//
|
|
averageDirection.Normalize();
|
|
sweepMesh.Direction = averageDirection.Coor;
|
|
//
|
|
bb = _geometry.GetBoundingBoxForNodeIds(surfaceVertices.ToArray());
|
|
sweepMesh.SweepCenter = bb.GetCenter();
|
|
// Round
|
|
double d = part.BoundingBox.GetDiagonal();
|
|
int digits = (int)Math.Log10(d) - 6;
|
|
if (digits > 0) digits = 0;
|
|
else if (digits < 0) digits *= -1;
|
|
//
|
|
sweepMesh.Direction[0] = Math.Round(sweepMesh.Direction[0], digits);
|
|
sweepMesh.Direction[1] = Math.Round(sweepMesh.Direction[1], digits);
|
|
sweepMesh.Direction[2] = Math.Round(sweepMesh.Direction[2], digits);
|
|
//
|
|
sweepMesh.SweepCenter[0] = Math.Round(sweepMesh.SweepCenter[0], digits);
|
|
sweepMesh.SweepCenter[1] = Math.Round(sweepMesh.SweepCenter[1], digits);
|
|
sweepMesh.SweepCenter[2] = Math.Round(sweepMesh.SweepCenter[2], digits);
|
|
//
|
|
int[][][] layerGroupEdgeIds = GetDirectionEdges(vis, surfaceIdSurfaceNeighbourIds,
|
|
sideSurfaceIds,
|
|
layerSideSurfaceIds);
|
|
// Gmsh numbering
|
|
for (int i = 0; i < sideSurfaceIds.Length; i++)
|
|
{
|
|
sideSurfaceIds[i] = FeMesh.GmshTopologyId(sideSurfaceIds[i], partId);
|
|
}
|
|
sweepMesh.SideSurfaceIds = sideSurfaceIds;
|
|
//
|
|
for (int i = 0; i < layerGroupEdgeIds.Length; i++)
|
|
{
|
|
for (int j = 0; j < layerGroupEdgeIds[i].Length; j++)
|
|
{
|
|
for (int z = 0; z < layerGroupEdgeIds[i][j].Length; z++)
|
|
{
|
|
layerGroupEdgeIds[i][j][z] =
|
|
FeMesh.GmshTopologyId(layerGroupEdgeIds[i][j][z], partId);
|
|
}
|
|
}
|
|
}
|
|
sweepMesh.LayerGroupEdgeIds = layerGroupEdgeIds;
|
|
}
|
|
else error = "The sweep side surfaces are not 4-sided surfaces.";
|
|
}
|
|
else error = "The sweep side surfaces do not form a closed loop.";
|
|
}
|
|
else error = "There are more than 1 target surfaces.";
|
|
|
|
// Get target surfaces
|
|
|
|
}
|
|
else error = "The selected surface/s contain free edge loop/s with a single edge (a hole or an extrusion).";
|
|
}
|
|
else error = "The selected surfaces are not connected.";
|
|
}
|
|
else error = "The surfaces of more than one part are selected.";
|
|
//
|
|
return error == null ? null : error + " The swept mesh cannot be created.";
|
|
}
|
|
private string IsRevolveMeshProperlyDefined(RevolveMesh revolveMesh)
|
|
{
|
|
string error = null;
|
|
revolveMesh.AxisDirection = null;
|
|
revolveMesh.AxisCenter = null;
|
|
revolveMesh.AngleDeg = -1;
|
|
revolveMesh.MiddleR = -1;
|
|
//
|
|
if (revolveMesh.AlgorithmMesh2D == CaeMesh.Meshing.GmshAlgorithmMesh2DEnum.QuasiStructuredQuad)
|
|
return "The revolve mesh setup item cannot use the quasi-structured quad algorithm.";
|
|
//
|
|
if (revolveMesh.ElementSizeType == CaeMesh.Meshing.ElementSizeTypeEnum.MultiLayerd &&
|
|
(revolveMesh.LayerSizes.Length != revolveMesh.NumOfElementsPerLayer.Length))
|
|
return "The number of layers must be equal for layer sizes and number of elements per layer.";
|
|
//
|
|
int[] selectedPartIds = FeMesh.GetPartIdsFromGeometryIds(revolveMesh.CreationIds);
|
|
//
|
|
if (selectedPartIds.Length == 1)
|
|
{
|
|
// Get part
|
|
int partId = selectedPartIds[0];
|
|
GeometryPart part = (GeometryPart)_geometry.GetPartFromId(partId);
|
|
if (IsPartCompoundSubPart(part.Name))
|
|
return "The revolve mesh setup item cannot be defined on a compound part.";
|
|
if (!part.IsCADPart)
|
|
return "The revolve mesh setup item cannot be defined on a stl based part.";
|
|
if (part.PartType != PartType.Solid && part.PartType != PartType.SolidAsShell)
|
|
return "The revolve mesh setup item cannot be defined on a shell part.";
|
|
//
|
|
VisualizationData vis = part.Visualization;
|
|
// Get surface ids
|
|
HashSet<int> surfaceIds = new HashSet<int>();
|
|
for (int i = 0; i < revolveMesh.CreationIds.Length; i++)
|
|
surfaceIds.Add(FeMesh.GetItemIdFromGeometryId(revolveMesh.CreationIds[i]));
|
|
// Do selected surfaces form a connected surface
|
|
if (vis.AreSurfacesConnected(surfaceIds.ToArray()))
|
|
{
|
|
Dictionary<int, HashSet<int>> vertexIdEdgeId = vis.GetVertexIdEdgeIds();
|
|
HashSet<int> surfaceVertices = vis.GetVertexNodeIdsForSurfaceIds(surfaceIds.ToArray());
|
|
HashSet<int> surfaceEdgeIds = vis.GetEdgeIdsForSurfaceIds(surfaceIds.ToArray());
|
|
//
|
|
HashSet<int> directionEdgeIds = new HashSet<int>();
|
|
foreach (var vertexId in surfaceVertices)
|
|
directionEdgeIds.UnionWith(vertexIdEdgeId[vertexId].Except(surfaceEdgeIds));
|
|
//
|
|
bool arcEdges = false;
|
|
if (vis.EdgeTypes != null)
|
|
{
|
|
HashSet<GeomCurveType> edgeTypes = new HashSet<GeomCurveType>();
|
|
foreach (var directionEdgeId in directionEdgeIds) edgeTypes.Add(vis.EdgeTypes[directionEdgeId]);
|
|
//
|
|
if (edgeTypes.Count() == 1 && edgeTypes.First() == GeomCurveType.Circle) arcEdges = true;
|
|
}
|
|
if (!arcEdges)
|
|
{
|
|
arcEdges = vis.IsEdgeRadiusConstant(directionEdgeIds, _geometry.Nodes);
|
|
}
|
|
if (arcEdges)
|
|
{
|
|
double r;
|
|
double arcAngleDeg;
|
|
double[] axisCenter;
|
|
double[] axisDirection;
|
|
vis.GetArcEdgeDataForEdgeIds(directionEdgeIds, surfaceVertices, _geometry.Nodes,
|
|
out r, out arcAngleDeg, out axisCenter, out axisDirection);
|
|
if (r > 0 && arcAngleDeg > 0)
|
|
{
|
|
revolveMesh.AxisDirection = axisDirection;
|
|
revolveMesh.AxisCenter = axisCenter;
|
|
revolveMesh.AngleDeg = arcAngleDeg;
|
|
revolveMesh.MiddleR = r;
|
|
// Round
|
|
double d = part.BoundingBox.GetDiagonal();
|
|
int digits = (int)Math.Log10(d) - 6;
|
|
if (digits > 0) digits = 0;
|
|
else if (digits < 0) digits *= -1;
|
|
//
|
|
revolveMesh.AxisDirection[0] = Math.Round(revolveMesh.AxisDirection[0], digits);
|
|
revolveMesh.AxisDirection[1] = Math.Round(revolveMesh.AxisDirection[1], digits);
|
|
revolveMesh.AxisDirection[2] = Math.Round(revolveMesh.AxisDirection[2], digits);
|
|
//
|
|
revolveMesh.AxisCenter[0] = Math.Round(revolveMesh.AxisCenter[0], digits);
|
|
revolveMesh.AxisCenter[1] = Math.Round(revolveMesh.AxisCenter[1], digits);
|
|
revolveMesh.AxisCenter[2] = Math.Round(revolveMesh.AxisCenter[2], digits);
|
|
//
|
|
revolveMesh.AngleDeg = Math.Round(revolveMesh.AngleDeg, 4);
|
|
//
|
|
revolveMesh.MiddleR = Math.Round(revolveMesh.MiddleR, digits);
|
|
}
|
|
else error = "The selected surfaces have direction edges with different arc angles or axes.";
|
|
}
|
|
else error = "The selected surfaces have direction edges of different arc radii.";
|
|
}
|
|
else error = "The selected surfaces are not connected.";
|
|
}
|
|
else error = "The surfaces of more than one part are selected.";
|
|
//
|
|
return error == null ? null : error + " The revolve mesh cannot be created.";
|
|
}
|
|
public bool IsPartCompoundSubPart(string partName)
|
|
{
|
|
if (_geometry == null) return false;
|
|
//
|
|
foreach (var entry in _geometry.Parts)
|
|
{
|
|
if (entry.Value is CompoundGeometryPart cgp)
|
|
{
|
|
if (cgp.SubPartNames.Contains(partName)) return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
private int[][][] GetDirectionEdges(VisualizationData vis, Dictionary<int, HashSet<int>> surfaceIdSurfaceNeighbourIds,
|
|
int[] surfaceIds, List<int[]> layerSurfaceIds)
|
|
{
|
|
// Split into groups
|
|
Node<int> node;
|
|
Graph<int> connections = new Graph<int>();
|
|
Dictionary<int, Node<int>> surfaceIdNode = new Dictionary<int, Node<int>>();
|
|
//
|
|
foreach (var surfaceId in surfaceIds)
|
|
{
|
|
node = new Node<int>(surfaceId);
|
|
connections.AddNode(node);
|
|
surfaceIdNode.Add(surfaceId, node);
|
|
}
|
|
HashSet<int> surfaceIdsHash = surfaceIds.ToHashSet();
|
|
foreach (var surfaceId in surfaceIds)
|
|
{
|
|
foreach (var neighbourId in surfaceIdSurfaceNeighbourIds[surfaceId])
|
|
{
|
|
if (surfaceIdsHash.Contains(neighbourId))
|
|
connections.AddUndirectedEdge(surfaceIdNode[surfaceId], surfaceIdNode[neighbourId]);
|
|
}
|
|
}
|
|
List<Graph<int>> subGraphs = connections.GetConnectedSubgraphs();
|
|
//
|
|
int count = 0;
|
|
HashSet<int>[] groupSurfaceId = new HashSet<int>[subGraphs.Count];
|
|
foreach (var subGraph in subGraphs) groupSurfaceId[count++] = subGraph.GetValues().ToHashSet();
|
|
// Create group mask
|
|
Dictionary<int, int> surfaceIdGroupId = new Dictionary<int, int>();
|
|
for (int i = 0; i < groupSurfaceId.Length; i++)
|
|
{
|
|
foreach (var surfaceId in groupSurfaceId[i])
|
|
{
|
|
surfaceIdGroupId.Add(surfaceId, i);
|
|
}
|
|
}
|
|
// Create layer mask
|
|
Dictionary<int, int> surfaceIdLayerId = new Dictionary<int, int>();
|
|
count = 0;
|
|
foreach (var layer in layerSurfaceIds)
|
|
{
|
|
for (int i = 0; i < layer.Length; i++)
|
|
{
|
|
surfaceIdLayerId.Add(layer[i], count);
|
|
}
|
|
count++;
|
|
}
|
|
//
|
|
HashSet<int>[][] layerGroupSurfaceIds = new HashSet<int>[layerSurfaceIds.Count][];
|
|
for (int i = 0; i < layerGroupSurfaceIds.Length; i++)
|
|
{
|
|
layerGroupSurfaceIds[i] = new HashSet<int>[groupSurfaceId.Length];
|
|
for (int j = 0; j < layerGroupSurfaceIds[i].Length; j++) layerGroupSurfaceIds[i][j] = new HashSet<int>();
|
|
}
|
|
//
|
|
int groupId;
|
|
int layerId;
|
|
for (int i = 0; i < surfaceIds.Length; i++)
|
|
{
|
|
groupId = surfaceIdGroupId[surfaceIds[i]];
|
|
layerId = surfaceIdLayerId[surfaceIds[i]];
|
|
layerGroupSurfaceIds[layerId][groupId].Add(surfaceIds[i]);
|
|
}
|
|
//
|
|
int[][][] layerGroupEdgeIds = new int[layerSurfaceIds.Count][][];
|
|
for (int i = 0; i < layerGroupEdgeIds.Length; i++)
|
|
{
|
|
layerGroupEdgeIds[i] = new int[groupSurfaceId.Length][];
|
|
for (int j = 0; j < layerGroupEdgeIds[i].Length; j++)
|
|
layerGroupEdgeIds[i][j] = GetSharedEdgeIds(vis, layerGroupSurfaceIds[i][j]);
|
|
}
|
|
//
|
|
return layerGroupEdgeIds;
|
|
}
|
|
private int[] GetSharedEdgeIds(VisualizationData vis, HashSet<int> surfaceIds)
|
|
{
|
|
int directionEdgeId;
|
|
Dictionary<int, int> edgeIdCount = new Dictionary<int, int>();
|
|
foreach (var surfaceId in surfaceIds)
|
|
{
|
|
if (vis.IsSurfaceACylinderLike(surfaceId, out directionEdgeId))
|
|
{
|
|
if (edgeIdCount.ContainsKey(directionEdgeId)) edgeIdCount[directionEdgeId] += 2;
|
|
else edgeIdCount[directionEdgeId] = 2;
|
|
}
|
|
else
|
|
{
|
|
foreach (var edgeId in vis.FaceEdgeIds[surfaceId])
|
|
{
|
|
if (edgeIdCount.ContainsKey(edgeId)) edgeIdCount[edgeId]++;
|
|
else edgeIdCount[edgeId] = 1;
|
|
}
|
|
}
|
|
}
|
|
List<int> edgeIds = new List<int>();
|
|
foreach (var entry in edgeIdCount)
|
|
{
|
|
if (entry.Value == 2) edgeIds.Add(entry.Key);
|
|
}
|
|
return edgeIds.ToArray();
|
|
}
|
|
// Springs
|
|
public PointSpringData[] GetPointSpringsFromSurfaceSpring(SurfaceSpring spring)
|
|
{
|
|
List<PointSpringData> springs = new List<PointSpringData>();
|
|
//
|
|
if (spring.GetSpringDirections().Length != 0)
|
|
{
|
|
double area;
|
|
Dictionary<int, double> nodalStiffnesses;
|
|
GetDistributedNodalValuesFromSurface(spring.RegionName, out nodalStiffnesses, out area);
|
|
//
|
|
if (spring.StiffnessPerArea) area = 1; // account for the stiffness type
|
|
//
|
|
double k1ByArea = spring.K1.Value / area;
|
|
double k2ByArea = spring.K2.Value / area;
|
|
double k3ByArea = spring.K3.Value / area;
|
|
//
|
|
foreach (var entry in nodalStiffnesses)
|
|
{
|
|
if (entry.Value != 0)
|
|
{
|
|
springs.Add(new PointSpringData(spring.Name + "_" + entry.Key.ToString(), entry.Key,
|
|
k1ByArea * entry.Value,
|
|
k2ByArea * entry.Value,
|
|
k3ByArea * entry.Value));
|
|
}
|
|
}
|
|
}
|
|
//
|
|
return springs.ToArray();
|
|
}
|
|
// Initial conditions
|
|
public InitialTemperature[] GetNodalTemperaturesFromVariableInitialTemperature(InitialTemperature initialTemperature)
|
|
{
|
|
if (initialTemperature.DistributionName == Distribution.DefaultDistributionName) return null;
|
|
// Node set
|
|
FeNodeSet nodeSet;
|
|
if (initialTemperature.RegionType == RegionTypeEnum.NodeSetName)
|
|
{
|
|
nodeSet = _mesh.NodeSets[initialTemperature.RegionName];
|
|
}
|
|
else if (initialTemperature.RegionType == RegionTypeEnum.SurfaceName)
|
|
{
|
|
FeSurface surface = _mesh.Surfaces[initialTemperature.RegionName];
|
|
nodeSet = _mesh.NodeSets[surface.NodeSetName];
|
|
}
|
|
else throw new NotSupportedException();
|
|
//
|
|
double[][] coor = new double[nodeSet.Labels.Length][];
|
|
// Parallel
|
|
Parallel.For(0, nodeSet.Labels.Length, i =>
|
|
{
|
|
int nodeId = nodeSet.Labels[i];
|
|
coor[i] = _mesh.Nodes[nodeId].Coor;
|
|
});
|
|
// Temperature
|
|
double[] temperatures;
|
|
initialTemperature.GetTemperaturesAndDistancesForPoints(this, coor, out _, out temperatures);
|
|
// Parallel
|
|
ConcurrentBag<InitialTemperature> initialTemperatures = new ConcurrentBag<InitialTemperature>();
|
|
Parallel.For(0, nodeSet.Labels.Length, i =>
|
|
{
|
|
int nodeId = nodeSet.Labels[i];
|
|
// Temperature conditions
|
|
InitialTemperature it = new InitialTemperature("NodeID_" + nodeId.ToString(), nodeId, temperatures[i], true);
|
|
initialTemperatures.Add(it);
|
|
});
|
|
//
|
|
return initialTemperatures.ToArray();
|
|
}
|
|
// Boundary conditions
|
|
public TemperatureBC[] GetNodalTemperaturesFromVariableTemperatureBC(TemperatureBC temperatureBC)
|
|
{
|
|
if (temperatureBC.DistributionName == Distribution.DefaultDistributionName) return null;
|
|
// Node set
|
|
FeNodeSet nodeSet;
|
|
if (temperatureBC.RegionType == RegionTypeEnum.NodeSetName)
|
|
{
|
|
nodeSet = _mesh.NodeSets[temperatureBC.RegionName];
|
|
}
|
|
else if (temperatureBC.RegionType == RegionTypeEnum.SurfaceName)
|
|
{
|
|
FeSurface surface = _mesh.Surfaces[temperatureBC.RegionName];
|
|
nodeSet = _mesh.NodeSets[surface.NodeSetName];
|
|
}
|
|
else throw new NotSupportedException();
|
|
//
|
|
double[][] coor = new double[nodeSet.Labels.Length][];
|
|
// Parallel
|
|
Parallel.For(0, nodeSet.Labels.Length, i =>
|
|
{
|
|
int nodeId = nodeSet.Labels[i];
|
|
coor[i] = _mesh.Nodes[nodeId].Coor;
|
|
});
|
|
// Temperature
|
|
double[] temperatures;
|
|
temperatureBC.GetTemperaturesAndDistancesForPoints(this, coor, out _, out temperatures);
|
|
// Parallel
|
|
ConcurrentBag<TemperatureBC> temperatureBCs = new ConcurrentBag<TemperatureBC>();
|
|
Parallel.For(0, nodeSet.Labels.Length, i =>
|
|
{
|
|
int nodeId = nodeSet.Labels[i];
|
|
// Temperature bcs
|
|
TemperatureBC tbc = new TemperatureBC("NodeID_" + nodeId.ToString(), nodeId, temperatures[i], true);
|
|
tbc.AmplitudeName = temperatureBC.AmplitudeName;
|
|
temperatureBCs.Add(tbc);
|
|
});
|
|
//
|
|
return temperatureBCs.ToArray();
|
|
}
|
|
// Loads
|
|
public double GetAreaForSTLoad(STLoad load)
|
|
{
|
|
int sectionId = 0;
|
|
double A;
|
|
double thickness;
|
|
double area = 0;
|
|
FeElement element;
|
|
Dictionary<int, int> elementIdSectionId;
|
|
Dictionary<int, double> sectionIdThickness = new Dictionary<int, double>();
|
|
// Get element thicknesses
|
|
GetSectionAssignments(out elementIdSectionId);
|
|
foreach (var entry in _sections)
|
|
{
|
|
thickness = entry.Value.Thickness.Value;
|
|
sectionIdThickness.Add(sectionId++, thickness);
|
|
}
|
|
//
|
|
FeSurface surface = _mesh.Surfaces[load.SurfaceName];
|
|
if (surface.ElementFaces == null) return 0;
|
|
//
|
|
foreach (var entry in surface.ElementFaces)
|
|
{
|
|
foreach (var elementId in _mesh.ElementSets[entry.Value].Labels)
|
|
{
|
|
element = _mesh.Elements[elementId];
|
|
A = element.GetArea(entry.Key, _mesh.Nodes);
|
|
// Is shell edge face
|
|
if (element is FeElement2D element2D && entry.Key != FeFaceName.S1 && entry.Key != FeFaceName.S2)
|
|
{
|
|
sectionId = elementIdSectionId[elementId];
|
|
if (sectionId == -1) throw new CaeException("Missing section assignment at element " + elementId +
|
|
" from part " + _mesh.GetPartFromId(element.PartId) + ".");
|
|
thickness = sectionIdThickness[sectionId];
|
|
A *= thickness;
|
|
}
|
|
area += A;
|
|
}
|
|
}
|
|
return area;
|
|
}
|
|
public CLoad[] GetNodalCLoadsFromSurfaceTraction(STLoad load)
|
|
{
|
|
if (load.DistributionName == Distribution.DefaultDistributionName)
|
|
return GetNodalCLoadsFromConstantSurfaceTraction(load);
|
|
else
|
|
return GetNodalCLoadsFromSurfaceTractionByDistribution(load);
|
|
}
|
|
public CLoad[] GetNodalCLoadsFromConstantSurfaceTraction(STLoad load)
|
|
{
|
|
List<CLoad> loads = new List<CLoad>();
|
|
//
|
|
if (load.FMagnitude.Value != 0)
|
|
{
|
|
double area;
|
|
Dictionary<int, double> nodalForces;
|
|
GetDistributedNodalValuesFromSurface(load.SurfaceName, out nodalForces, out area);
|
|
//
|
|
double f1ByArea = load.F1.Value / area;
|
|
double f2ByArea = load.F2.Value / area;
|
|
double f3ByArea = load.F3.Value / area;
|
|
//
|
|
CLoad cLoad;
|
|
double phaseDeg = load.PhaseDeg.Value;
|
|
foreach (var entry in nodalForces)
|
|
{
|
|
if (entry.Value != 0)
|
|
{
|
|
cLoad = new CLoad("_CLoad_" + entry.Key.ToString(), entry.Key,
|
|
f1ByArea * entry.Value,
|
|
f2ByArea * entry.Value,
|
|
f3ByArea * entry.Value, load.TwoD, load.Complex, phaseDeg, true);
|
|
cLoad.AmplitudeName = load.AmplitudeName;
|
|
loads.Add(cLoad);
|
|
}
|
|
}
|
|
}
|
|
//
|
|
return loads.ToArray();
|
|
}
|
|
public CLoad[] GetNodalCLoadsFromSurfaceTractionByDistribution(STLoad load)
|
|
{
|
|
Distribution distribution = _distributions[load.DistributionName];
|
|
Dictionary<int, int> elementIdSectionId;
|
|
double[] sectionIdThickness = new double[_sections.Count];
|
|
// Get element thicknesses
|
|
GetSectionAssignments(out elementIdSectionId);
|
|
//
|
|
int sectionId = 0;
|
|
double thickness;
|
|
string surfaceName = load.SurfaceName;
|
|
foreach (var entry in _sections)
|
|
{
|
|
thickness = entry.Value.Thickness.Value;
|
|
sectionIdThickness[sectionId++] = thickness;
|
|
}
|
|
// Surface
|
|
FeSurface surface = _mesh.Surfaces[surfaceName];
|
|
if (surface.ElementFaces == null) return null;
|
|
//
|
|
int nodeId;
|
|
int[] nodeIds;
|
|
double A;
|
|
double area = 0;
|
|
double[] magnitude;
|
|
double[] force;
|
|
double[] nodalForce;
|
|
double[] faceNormal;
|
|
double[][] magnitudeByComponent;
|
|
double[][] nodalForceMagnitudes;
|
|
FeElement element;
|
|
Dictionary<int, double[]> nodeIdForce = new Dictionary<int, double[]>();
|
|
// Collect all node ids - speed up
|
|
HashSet<int> allNodeIdsHash = new HashSet<int>();
|
|
foreach (var entry in surface.ElementFaces)
|
|
{
|
|
foreach (var elementId in _mesh.ElementSets[entry.Value].Labels)
|
|
{
|
|
element = _mesh.Elements[elementId];
|
|
allNodeIdsHash.UnionWith(element.GetNodeIdsFromFaceName(entry.Key));
|
|
}
|
|
}
|
|
// Collect all points - speed up
|
|
int[] allNodeIds = allNodeIdsHash.ToArray();
|
|
double[][] points = new double[allNodeIds.Length][];
|
|
for (int i = 0; i < allNodeIds.Length; i++)
|
|
{
|
|
nodeId = allNodeIds[i];
|
|
points[i] = _mesh.Nodes[nodeId].Coor;
|
|
}
|
|
// Get all nodal values
|
|
double[][] values;
|
|
distribution.GetMagnitudesAndDistancesForPoints(this, points, out values, out _);
|
|
// Get all nodal values as dictionary - speed up
|
|
Dictionary<int, double[]> nodeIdValues = new Dictionary<int, double[]>();
|
|
for (int i = 0; i < allNodeIds.Length; i++) nodeIdValues.Add(allNodeIds[i], values[i]);
|
|
//
|
|
foreach (var entry in surface.ElementFaces)
|
|
{
|
|
foreach (var elementId in _mesh.ElementSets[entry.Value].Labels)
|
|
{
|
|
element = _mesh.Elements[elementId];
|
|
// Node ids
|
|
nodeIds = element.GetNodeIdsFromFaceName(entry.Key);
|
|
//
|
|
_mesh.GetElementFaceCenterAndNormal(elementId, entry.Key, out double[] faceCenter, out faceNormal,
|
|
out bool shellElement);
|
|
//
|
|
A = element.GetArea(entry.Key, _mesh.Nodes);
|
|
// Account for 2D area when an edge is selected
|
|
if (element is FeElement2D element2D && entry.Key != FeFaceName.S1 && entry.Key != FeFaceName.S2)
|
|
{
|
|
sectionId = elementIdSectionId[elementId];
|
|
if (sectionId == -1) throw new CaeException("Missing section assignment at element " + elementId +
|
|
" from part " + _mesh.GetPartFromId(element.PartId) + ".");
|
|
thickness = sectionIdThickness[sectionId];
|
|
A *= thickness;
|
|
}
|
|
// Force magnitude
|
|
magnitudeByComponent = new double[distribution.NumOfComponents][];
|
|
for (int i = 0; i < nodeIds.Length; i++)
|
|
{
|
|
nodeId = nodeIds[i];
|
|
magnitude = nodeIdValues[nodeId];
|
|
//
|
|
for (int j = 0; j < magnitudeByComponent.Length; j++)
|
|
{
|
|
if (magnitudeByComponent[j] == null) magnitudeByComponent[j] = new double[nodeIds.Length];
|
|
magnitudeByComponent[j][i] = magnitude[j];
|
|
}
|
|
}
|
|
// Force magnitudes without area
|
|
nodalForceMagnitudes = new double[distribution.NumOfComponents][];
|
|
for (int i = 0; i < nodalForceMagnitudes.Length; i++)
|
|
{
|
|
nodalForceMagnitudes[i] = element.GetEquivalentForcesFromFaceName(entry.Key, magnitudeByComponent[i]);
|
|
}
|
|
// Force vectors
|
|
for (int i = 0; i < nodeIds.Length; i++)
|
|
{
|
|
if (distribution.NumOfComponents == 1)
|
|
{
|
|
force = new double[] { A * nodalForceMagnitudes[0][i],
|
|
A * nodalForceMagnitudes[0][i],
|
|
A * nodalForceMagnitudes[0][i] };
|
|
}
|
|
else if (distribution.NumOfComponents == 3)
|
|
{
|
|
force = new double[] { A * nodalForceMagnitudes[0][i],
|
|
A * nodalForceMagnitudes[1][i],
|
|
A * nodalForceMagnitudes[2][i] };
|
|
}
|
|
else throw new NotSupportedException();
|
|
//
|
|
if (!nodeIdForce.TryGetValue(nodeIds[i], out nodalForce))
|
|
{
|
|
nodalForce = new double[3];
|
|
nodeIdForce.Add(nodeIds[i], nodalForce);
|
|
}
|
|
nodalForce[0] += force[0];
|
|
nodalForce[1] += force[1];
|
|
nodalForce[2] += force[2];
|
|
}
|
|
//
|
|
area += A;
|
|
}
|
|
}
|
|
// Concentrated loads
|
|
CLoad cLoad;
|
|
List<CLoad> loads = new List<CLoad>();
|
|
double phaseDeg = load.PhaseDeg.Value;
|
|
double p1ByArea = load.P1.Value;
|
|
double p2ByArea = load.P2.Value;
|
|
double p3ByArea = load.P3.Value;
|
|
foreach (var entry in nodeIdForce)
|
|
{
|
|
if (entry.Value[0] != 0 || entry.Value[1] != 0 || entry.Value[2] != 0)
|
|
{
|
|
cLoad = new CLoad("_CLoad_" + entry.Key.ToString(), entry.Key,
|
|
p1ByArea * entry.Value[0],
|
|
p2ByArea * entry.Value[1],
|
|
p3ByArea * entry.Value[2],
|
|
load.TwoD, load.Complex, phaseDeg, true);
|
|
//
|
|
cLoad.AmplitudeName = load.AmplitudeName;
|
|
//
|
|
loads.Add(cLoad);
|
|
}
|
|
}
|
|
//
|
|
return loads.ToArray();
|
|
}
|
|
public void GetDistributedNodalValuesFromSurface(string surfaceName,
|
|
out Dictionary<int, double> nodalValues,
|
|
out double area)
|
|
{
|
|
nodalValues = new Dictionary<int, double>();
|
|
area = 0;
|
|
//
|
|
int nodeId;
|
|
int sectionId = 0;
|
|
int[] nodeIds;
|
|
double A;
|
|
double thickness;
|
|
double[] equValue;
|
|
FeElement element;
|
|
Dictionary<int, int> elementIdSectionId;
|
|
Dictionary<int, double> sectionIdThickness = new Dictionary<int, double>();
|
|
// Get element thicknesses
|
|
GetSectionAssignments(out elementIdSectionId);
|
|
foreach (var entry in _sections)
|
|
{
|
|
thickness = entry.Value.Thickness.Value;
|
|
sectionIdThickness.Add(sectionId++, thickness);
|
|
}
|
|
//
|
|
FeSurface surface = _mesh.Surfaces[surfaceName];
|
|
if (surface.ElementFaces == null) return;
|
|
//
|
|
foreach (var entry in surface.ElementFaces)
|
|
{
|
|
foreach (var elementId in _mesh.ElementSets[entry.Value].Labels)
|
|
{
|
|
element = _mesh.Elements[elementId];
|
|
A = element.GetArea(entry.Key, _mesh.Nodes);
|
|
// Is shell edge face
|
|
if (element is FeElement2D element2D && entry.Key != FeFaceName.S1 && entry.Key != FeFaceName.S2)
|
|
{
|
|
sectionId = elementIdSectionId[elementId];
|
|
if (sectionId == -1) throw new CaeException("Missing section assignment at element " + elementId +
|
|
" from part " + _mesh.GetPartFromId(element.PartId) + ".");
|
|
thickness = sectionIdThickness[sectionId];
|
|
A *= thickness;
|
|
}
|
|
area += A;
|
|
nodeIds = element.GetNodeIdsFromFaceName(entry.Key);
|
|
equValue = element.GetEquivalentForcesFromFaceName(entry.Key);
|
|
//
|
|
for (int i = 0; i < nodeIds.Length; i++)
|
|
{
|
|
nodeId = nodeIds[i];
|
|
if (nodalValues.ContainsKey(nodeId)) nodalValues[nodeId] += A * equValue[i];
|
|
else nodalValues.Add(nodeId, A * equValue[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
public CLoad[] GetNodalCLoadsFromImportedSurfaceTraction(ImportedSTLoad load)
|
|
{
|
|
Dictionary<int, int> elementIdSectionId;
|
|
double[] sectionIdThickness = new double[_sections.Count];
|
|
// Get element thicknesses
|
|
GetSectionAssignments(out elementIdSectionId);
|
|
//
|
|
int sectionId = 0;
|
|
double thickness;
|
|
string surfaceName = load.SurfaceName;
|
|
foreach (var entry in _sections)
|
|
{
|
|
thickness = entry.Value.Thickness.Value;
|
|
sectionIdThickness[sectionId++] = thickness;
|
|
}
|
|
// Surface
|
|
FeSurface surface = _mesh.Surfaces[surfaceName];
|
|
if (surface.ElementFaces == null) return null;
|
|
//
|
|
int nodeId;
|
|
int[] nodeIds;
|
|
double A;
|
|
double[] forcePerArea;
|
|
double[] force;
|
|
double[] nodalForce;
|
|
double[] faceNormal;
|
|
double[][] forcePerAreaByValueId;
|
|
double[][] nodalForceMagnitudes;
|
|
FeElement element;
|
|
Dictionary<int, double[]> nodeIdForcePerArea = new Dictionary<int, double[]>();
|
|
Dictionary<int, double[]> nodeIdForce = new Dictionary<int, double[]>();
|
|
//
|
|
foreach (var entry in surface.ElementFaces)
|
|
{
|
|
foreach (var elementId in _mesh.ElementSets[entry.Value].Labels)
|
|
{
|
|
element = _mesh.Elements[elementId];
|
|
// Node ids
|
|
nodeIds = element.GetNodeIdsFromFaceName(entry.Key);
|
|
//
|
|
_mesh.GetElementFaceCenterAndNormal(elementId, entry.Key, out double[] faceCenter, out faceNormal,
|
|
out bool shellElement);
|
|
//
|
|
A = element.GetArea(entry.Key, _mesh.Nodes);
|
|
// Account for 2D area when an edge is selected
|
|
if (element is FeElement2D element2D && entry.Key != FeFaceName.S1 && entry.Key != FeFaceName.S2)
|
|
{
|
|
sectionId = elementIdSectionId[elementId];
|
|
if (sectionId == -1) throw new CaeException("Missing section assignment at element " + elementId +
|
|
" from part " + _mesh.GetPartFromId(element.PartId) + ".");
|
|
thickness = sectionIdThickness[sectionId];
|
|
A *= thickness;
|
|
}
|
|
// Force per area
|
|
forcePerAreaByValueId = new double[load.Interpolator.NumOfValues][];
|
|
for (int i = 0; i < nodeIds.Length; i++)
|
|
{
|
|
nodeId = nodeIds[i];
|
|
if (!nodeIdForcePerArea.TryGetValue(nodeId, out forcePerArea))
|
|
{
|
|
forcePerArea = load.GetForcePerAreaForPoint(_mesh.Nodes[nodeId].Coor);
|
|
nodeIdForcePerArea.Add(nodeId, forcePerArea);
|
|
}
|
|
for (int j = 0; j < forcePerAreaByValueId.Length; j++)
|
|
{
|
|
if (forcePerAreaByValueId[j] == null) forcePerAreaByValueId[j] = new double[nodeIds.Length];
|
|
forcePerAreaByValueId[j][i] = forcePerArea[j];
|
|
}
|
|
}
|
|
// Force magnitudes without area
|
|
nodalForceMagnitudes = new double[load.Interpolator.NumOfValues][];
|
|
for (int i = 0; i < nodalForceMagnitudes.Length; i++)
|
|
{
|
|
nodalForceMagnitudes[i] = element.GetEquivalentForcesFromFaceName(entry.Key, forcePerAreaByValueId[i]);
|
|
}
|
|
// Force vectors
|
|
for (int i = 0; i < nodeIds.Length; i++)
|
|
{
|
|
force = new double[] { A * nodalForceMagnitudes[0][i],
|
|
A * nodalForceMagnitudes[1][i],
|
|
A * nodalForceMagnitudes[2][i] };
|
|
//
|
|
if (!nodeIdForce.TryGetValue(nodeIds[i], out nodalForce))
|
|
{
|
|
nodalForce = new double[3];
|
|
nodeIdForce.Add(nodeIds[i], nodalForce);
|
|
}
|
|
nodalForce[0] += force[0];
|
|
nodalForce[1] += force[1];
|
|
nodalForce[2] += force[2];
|
|
}
|
|
|
|
}
|
|
}
|
|
// Concentrated loads
|
|
CLoad cLoad;
|
|
List<CLoad> loads = new List<CLoad>();
|
|
double phaseDeg = load.PhaseDeg.Value;
|
|
foreach (var entry in nodeIdForce)
|
|
{
|
|
if (entry.Value[0] != 0 || entry.Value[1] != 0 || entry.Value[2] != 0)
|
|
{
|
|
cLoad = new CLoad("_CLoad_" + entry.Key.ToString(), entry.Key,
|
|
entry.Value[0],
|
|
entry.Value[1],
|
|
entry.Value[2],
|
|
load.TwoD, load.Complex, phaseDeg, true);
|
|
//
|
|
cLoad.AmplitudeName = load.AmplitudeName;
|
|
//
|
|
loads.Add(cLoad);
|
|
}
|
|
}
|
|
//
|
|
return loads.ToArray();
|
|
}
|
|
public CLoad[] GetNodalCLoadsFromVariablePressureLoad(VariablePressure load)
|
|
{
|
|
Dictionary<int, int> elementIdSectionId;
|
|
double[] sectionIdThickness = new double[_sections.Count];
|
|
// Get element thicknesses
|
|
GetSectionAssignments(out elementIdSectionId);
|
|
//
|
|
int sectionId = 0;
|
|
double thickness;
|
|
string surfaceName = load.SurfaceName;
|
|
foreach (var entry in _sections)
|
|
{
|
|
thickness = entry.Value.Thickness.Value;
|
|
sectionIdThickness[sectionId++] = thickness;
|
|
}
|
|
// Surface
|
|
FeSurface surface = _mesh.Surfaces[surfaceName];
|
|
if (surface.ElementFaces == null) return null;
|
|
//
|
|
int nodeId = -1;
|
|
int[] nodeIds;
|
|
double A;
|
|
double sign;
|
|
double pressure;
|
|
double[] force;
|
|
double[] nodalForce;
|
|
double[] faceNormal;
|
|
double[] nodalPressures;
|
|
double[] nodalForceMagnitudes;
|
|
FeElement element;
|
|
Dictionary<int, double> nodeIdPressure = new Dictionary<int, double>();
|
|
Dictionary<int, double[]> nodeIdForce = new Dictionary<int, double[]>();
|
|
//
|
|
FeFaceName faceName;
|
|
FeElement expandedElement;
|
|
int[] expandedNodeIds;
|
|
Dictionary<int, FeNode> expandedNodes;
|
|
//
|
|
foreach (var entry in surface.ElementFaces)
|
|
{
|
|
foreach (var elementId in _mesh.ElementSets[entry.Value].Labels)
|
|
{
|
|
element = _mesh.Elements[elementId];
|
|
// Node ids
|
|
nodeIds = element.GetNodeIdsFromFaceName(entry.Key);
|
|
//
|
|
_mesh.GetElementFaceCenterAndNormal(elementId, entry.Key, out double[] faceCenter, out faceNormal,
|
|
out bool shellElement);
|
|
//
|
|
if (Properties.ModelSpace == ModelSpaceEnum.Axisymmetric)
|
|
{
|
|
GetExpandedAxisymmetricElementFromNodeIds(nodeIds, out expandedElement, out expandedNodes);
|
|
expandedNodeIds = expandedElement.NodeIds;
|
|
faceName = FeFaceName.S1;
|
|
//
|
|
A = expandedElement.GetArea(faceName, expandedNodes) * 180 / 0.01;
|
|
// Pressure
|
|
nodalPressures = new double[expandedNodeIds.Length];
|
|
for (int i = 0; i < nodalPressures.Length; i++)
|
|
{
|
|
nodeId = expandedNodeIds[i];
|
|
pressure = load.GetPressureForPoint(this, expandedNodes[nodeId].Coor);
|
|
nodalPressures[i] = pressure;
|
|
}
|
|
// Force magnitudes without area
|
|
nodalForceMagnitudes = expandedElement.GetEquivalentForcesFromFaceName(faceName, nodalPressures);
|
|
// Invert face normal in case of S1 or S2 shell face
|
|
sign = 1;
|
|
// Force vectors
|
|
for (int i = 0; i < expandedNodeIds.Length; i++)
|
|
{
|
|
force = new double[] {
|
|
sign * A * nodalForceMagnitudes[i] * faceNormal[0],
|
|
sign * A * nodalForceMagnitudes[i] * faceNormal[1],
|
|
sign * A * nodalForceMagnitudes[i] * faceNormal[2]};
|
|
//
|
|
if (expandedElement.Id == 31)
|
|
{
|
|
if (expandedNodeIds[i] == 1) nodeId = nodeIds[0];
|
|
else if (expandedNodeIds[i] == 2) nodeId = nodeIds[1];
|
|
else if (expandedNodeIds[i] == 3) nodeId = nodeIds[1];
|
|
}
|
|
else if (expandedElement.Id == 32)
|
|
{
|
|
if (expandedNodeIds[i] == 1) nodeId = nodeIds[1];
|
|
else if (expandedNodeIds[i] == 2) nodeId = nodeIds[0];
|
|
else if (expandedNodeIds[i] == 3) nodeId = nodeIds[0];
|
|
}
|
|
else if (expandedElement.Id == 41)
|
|
{
|
|
if (expandedNodeIds[i] == 1) nodeId = nodeIds[0];
|
|
else if (expandedNodeIds[i] == 2) nodeId = nodeIds[1];
|
|
else if (expandedNodeIds[i] == 3) nodeId = nodeIds[1];
|
|
else if (expandedNodeIds[i] == 4) nodeId = nodeIds[0];
|
|
}
|
|
else if (expandedElement.Id == 61)
|
|
{
|
|
if (expandedNodeIds[i] == 1) nodeId = nodeIds[0];
|
|
else if (expandedNodeIds[i] == 2) nodeId = nodeIds[1];
|
|
else if (expandedNodeIds[i] == 3) nodeId = nodeIds[1];
|
|
else if (expandedNodeIds[i] == 4) nodeId = nodeIds[2];
|
|
else if (expandedNodeIds[i] == 5) nodeId = nodeIds[1];
|
|
else if (expandedNodeIds[i] == 6) nodeId = nodeIds[2];
|
|
}
|
|
else if (expandedElement.Id == 62)
|
|
{
|
|
if (expandedNodeIds[i] == 1) nodeId = nodeIds[1];
|
|
else if (expandedNodeIds[i] == 2) nodeId = nodeIds[0];
|
|
else if (expandedNodeIds[i] == 3) nodeId = nodeIds[0];
|
|
else if (expandedNodeIds[i] == 4) nodeId = nodeIds[2];
|
|
else if (expandedNodeIds[i] == 5) nodeId = nodeIds[0];
|
|
else if (expandedNodeIds[i] == 6) nodeId = nodeIds[2];
|
|
}
|
|
else if (expandedElement.Id == 81)
|
|
{
|
|
if (expandedNodeIds[i] == 1) nodeId = nodeIds[0];
|
|
else if (expandedNodeIds[i] == 2) nodeId = nodeIds[1];
|
|
else if (expandedNodeIds[i] == 3) nodeId = nodeIds[1];
|
|
else if (expandedNodeIds[i] == 4) nodeId = nodeIds[0];
|
|
else if (expandedNodeIds[i] == 5) nodeId = nodeIds[2];
|
|
else if (expandedNodeIds[i] == 6) nodeId = nodeIds[1];
|
|
else if (expandedNodeIds[i] == 7) nodeId = nodeIds[2];
|
|
else if (expandedNodeIds[i] == 8) nodeId = nodeIds[0];
|
|
}
|
|
//
|
|
if (!nodeIdForce.TryGetValue(nodeId, out nodalForce))
|
|
{
|
|
nodalForce = new double[3];
|
|
nodeIdForce.Add(nodeId, nodalForce);
|
|
}
|
|
nodalForce[0] += force[0];
|
|
nodalForce[1] += force[1];
|
|
nodalForce[2] += force[2];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
A = element.GetArea(entry.Key, _mesh.Nodes);
|
|
// Account for 2D area when an edge is selected
|
|
if (element is FeElement2D element2D && entry.Key != FeFaceName.S1 && entry.Key != FeFaceName.S2)
|
|
{
|
|
sectionId = elementIdSectionId[elementId];
|
|
if (sectionId == -1) throw new CaeException("Missing section assignment at element " + elementId +
|
|
" from part " + _mesh.GetPartFromId(element.PartId) + ".");
|
|
thickness = sectionIdThickness[sectionId];
|
|
A *= thickness;
|
|
}
|
|
// Pressure
|
|
nodalPressures = new double[nodeIds.Length];
|
|
for (int i = 0; i < nodalPressures.Length; i++)
|
|
{
|
|
nodeId = nodeIds[i];
|
|
if (!nodeIdPressure.TryGetValue(nodeId, out pressure))
|
|
{
|
|
pressure = load.GetPressureForPoint(this, _mesh.Nodes[nodeId].Coor);
|
|
nodeIdPressure.Add(nodeId, pressure);
|
|
}
|
|
nodalPressures[i] = pressure;
|
|
}
|
|
// Force magnitudes without area
|
|
nodalForceMagnitudes = element.GetEquivalentForcesFromFaceName(entry.Key, nodalPressures);
|
|
// Invert face normal in case of S1 or S2 shell face
|
|
if (shellElement && (entry.Key == FeFaceName.S1 || entry.Key == FeFaceName.S2)) sign = -1;
|
|
else sign = 1;
|
|
// Force vectors
|
|
for (int i = 0; i < nodeIds.Length; i++)
|
|
{
|
|
force = new double[] {
|
|
sign * A * nodalForceMagnitudes[i] * faceNormal[0],
|
|
sign * A * nodalForceMagnitudes[i] * faceNormal[1],
|
|
sign * A * nodalForceMagnitudes[i] * faceNormal[2]};
|
|
//
|
|
if (!nodeIdForce.TryGetValue(nodeIds[i], out nodalForce))
|
|
{
|
|
nodalForce = new double[3];
|
|
nodeIdForce.Add(nodeIds[i], nodalForce);
|
|
}
|
|
nodalForce[0] += force[0];
|
|
nodalForce[1] += force[1];
|
|
nodalForce[2] += force[2];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Concentrated loads
|
|
CLoad cLoad;
|
|
List<CLoad> loads = new List<CLoad>();
|
|
double phaseDeg = load.PhaseDeg.Value;
|
|
foreach (var entry in nodeIdForce)
|
|
{
|
|
if (entry.Value[0] != 0 || entry.Value[1] != 0 || entry.Value[2] != 0)
|
|
{
|
|
cLoad = new CLoad("_CLoad_" + entry.Key.ToString(), entry.Key,
|
|
entry.Value[0],
|
|
entry.Value[1],
|
|
entry.Value[2],
|
|
load.TwoD, load.Complex, phaseDeg, true);
|
|
//
|
|
cLoad.AmplitudeName = load.AmplitudeName;
|
|
//
|
|
loads.Add(cLoad);
|
|
}
|
|
}
|
|
//
|
|
return loads.ToArray();
|
|
}
|
|
public void GetExpandedAxisymmetricElementFromNodeIds(int[] nodeIds, out FeElement expandedElement,
|
|
out Dictionary<int, FeNode> expandedNodes)
|
|
{
|
|
expandedElement = null;
|
|
expandedNodes = new Dictionary<int, FeNode>();
|
|
// Node coor
|
|
double[][] coor = new double[nodeIds.Length][];
|
|
for (int i = 0; i < nodeIds.Length; i++) coor[i] = _mesh.Nodes[nodeIds[i]].Coor;
|
|
//
|
|
double cos = Math.Cos(Math.PI / 180 * 0.01);
|
|
double sin = Math.Sin(Math.PI / 180 * 0.01);
|
|
//
|
|
if (nodeIds.Length == 2)
|
|
{
|
|
// Triangle
|
|
if (coor[0][0] == 0)
|
|
{
|
|
expandedNodes.Add(1, new FeNode(1, coor[0]));
|
|
expandedNodes.Add(2, new FeNode(2, coor[1][0] * cos, coor[1][1], coor[1][0] * sin));
|
|
expandedNodes.Add(3, new FeNode(3, coor[1][0] * cos, coor[1][1], -coor[1][0] * sin));
|
|
expandedElement = new LinearTriangleElement(31, new int[] { 1, 2, 3 });
|
|
}
|
|
// Triangle
|
|
else if (coor[1][0] == 0)
|
|
{
|
|
expandedNodes.Add(1, new FeNode(1, coor[1]));
|
|
expandedNodes.Add(2, new FeNode(2, coor[0][0] * cos, coor[0][1], coor[0][0] * sin));
|
|
expandedNodes.Add(3, new FeNode(3, coor[0][0] * cos, coor[0][1], -coor[0][0] * sin));
|
|
expandedElement = new LinearTriangleElement(32, new int[] { 1, 2, 3 });
|
|
}
|
|
// Quad
|
|
else
|
|
{
|
|
expandedNodes.Add(1, new FeNode(1, coor[0][0] * cos, coor[0][1], coor[0][0] * sin));
|
|
expandedNodes.Add(2, new FeNode(2, coor[1][0] * cos, coor[1][1], coor[1][0] * sin));
|
|
expandedNodes.Add(3, new FeNode(3, coor[1][0] * cos, coor[1][1], -coor[1][0] * sin));
|
|
expandedNodes.Add(4, new FeNode(4, coor[0][0] * cos, coor[0][1], -coor[0][0] * sin));
|
|
expandedElement = new LinearQuadrilateralElement(41, new int[] { 1, 2, 3, 4 });
|
|
}
|
|
}
|
|
else if (nodeIds.Length == 3)
|
|
{
|
|
// Triangle
|
|
if (coor[0][0] == 0)
|
|
{
|
|
expandedNodes.Add(1, new FeNode(1, coor[0]));
|
|
expandedNodes.Add(2, new FeNode(2, coor[1][0] * cos, coor[1][1], coor[1][0] * sin));
|
|
expandedNodes.Add(3, new FeNode(3, coor[1][0] * cos, coor[1][1], -coor[1][0] * sin));
|
|
//
|
|
expandedNodes.Add(4, new FeNode(4, coor[2][0] * cos, coor[2][1], coor[2][0] * sin));
|
|
expandedNodes.Add(5, new FeNode(5, coor[1]));
|
|
expandedNodes.Add(6, new FeNode(6, coor[2][0] * cos, coor[2][1], -coor[2][0] * sin));
|
|
//
|
|
expandedElement = new ParabolicTriangleElement(61, new int[] { 1, 2, 3, 4, 5, 6 });
|
|
}
|
|
// Triangle
|
|
else if (coor[1][0] == 0)
|
|
{
|
|
expandedNodes.Add(1, new FeNode(1, coor[1]));
|
|
expandedNodes.Add(2, new FeNode(2, coor[0][0] * cos, coor[0][1], coor[0][0] * sin));
|
|
expandedNodes.Add(3, new FeNode(3, coor[0][0] * cos, coor[0][1], -coor[0][0] * sin));
|
|
//
|
|
expandedNodes.Add(4, new FeNode(4, coor[2][0] * cos, coor[2][1], coor[2][0] * sin));
|
|
expandedNodes.Add(5, new FeNode(5, coor[0]));
|
|
expandedNodes.Add(6, new FeNode(6, coor[2][0] * cos, coor[2][1], -coor[2][0] * sin));
|
|
//
|
|
expandedElement = new ParabolicTriangleElement(62, new int[] { 1, 2, 3, 4, 5, 6 });
|
|
}
|
|
// Quad
|
|
else
|
|
{
|
|
expandedNodes.Add(1, new FeNode(1, coor[0][0] * cos, coor[0][1], coor[0][0] * sin));
|
|
expandedNodes.Add(2, new FeNode(2, coor[1][0] * cos, coor[1][1], coor[1][0] * sin));
|
|
expandedNodes.Add(3, new FeNode(3, coor[1][0] * cos, coor[1][1], -coor[1][0] * sin));
|
|
expandedNodes.Add(4, new FeNode(4, coor[0][0] * cos, coor[0][1], -coor[0][0] * sin));
|
|
//
|
|
expandedNodes.Add(5, new FeNode(5, coor[2][0] * cos, coor[2][1], coor[2][0] * sin));
|
|
expandedNodes.Add(6, new FeNode(6, coor[1]));
|
|
expandedNodes.Add(7, new FeNode(7, coor[2][0] * cos, coor[2][1], -coor[2][0] * sin));
|
|
expandedNodes.Add(8, new FeNode(8, coor[0]));
|
|
//
|
|
expandedElement = new ParabolicQuadrilateralElement(81, new int[] { 1, 2, 3, 4, 5, 6, 7, 8 });
|
|
}
|
|
}
|
|
else throw new NotSupportedException();
|
|
}
|
|
public DLoad[] GetElementDLoadsFromVariablePressureLoad(VariablePressure load)
|
|
{
|
|
// Surface
|
|
FeSurface surface = _mesh.Surfaces[load.SurfaceName];
|
|
if (surface.ElementFaces == null) return null;
|
|
//
|
|
double[][] faceCenters;
|
|
ConcurrentBag<DLoad> loads = new ConcurrentBag<DLoad>();
|
|
//
|
|
foreach (var entry in surface.ElementFaces) // this are faces S1, S2, ...
|
|
{
|
|
// Parallel
|
|
faceCenters = new double[_mesh.ElementSets[entry.Value].Labels.Length][];
|
|
Parallel.For(0, _mesh.ElementSets[entry.Value].Labels.Length, i =>
|
|
//for (int i = 0; i < _mesh.ElementSets[entry.Value].Labels.Length; i++)
|
|
{
|
|
int elementId = _mesh.ElementSets[entry.Value].Labels[i];
|
|
_mesh.GetElementFaceCenterAndNormal(elementId, entry.Key, out faceCenters[i], out _, out _);
|
|
}
|
|
);
|
|
// Pressure
|
|
double[] pressure = load.GetPressuresForPoints(this, faceCenters);
|
|
// Parallel
|
|
Parallel.For(0, _mesh.ElementSets[entry.Value].Labels.Length, i =>
|
|
//for (int i = 0; i < _mesh.ElementSets[entry.Value].Labels.Length; i++)
|
|
{
|
|
int elementId = _mesh.ElementSets[entry.Value].Labels[i];
|
|
// Pressure loads
|
|
if (pressure[i] != 0)
|
|
{
|
|
DLoad dLoad = new DLoad("ElementID_" + elementId.ToString(), elementId, entry.Key, pressure[i], load.TwoD,
|
|
load.Complex, load.PhaseDeg.Value, true);
|
|
dLoad.AmplitudeName = load.AmplitudeName;
|
|
loads.Add(dLoad);
|
|
}
|
|
}
|
|
);
|
|
}
|
|
//
|
|
return loads.ToArray();
|
|
}
|
|
public InitialTranslationalVelocity[] GetTranslationalVelocities(InitialAngularVelocity initialAngularVelocity,
|
|
Dictionary<string, int[]> referencePointsNodeIds)
|
|
{
|
|
Dictionary<int, double[]> nodeIdCoor = new Dictionary<int, double[]>();
|
|
if (initialAngularVelocity.RegionType == RegionTypeEnum.NodeSetName)
|
|
{
|
|
FeNodeSet nodeSet = _mesh.NodeSets[initialAngularVelocity.RegionName];
|
|
for (int i = 0; i < nodeSet.Labels.Length; i++)
|
|
{
|
|
nodeIdCoor[nodeSet.Labels[i]] = _mesh.Nodes[nodeSet.Labels[i]].Coor;
|
|
}
|
|
}
|
|
else if (initialAngularVelocity.RegionType == RegionTypeEnum.SurfaceName)
|
|
{
|
|
string nodeSetName = _mesh.Surfaces[initialAngularVelocity.RegionName].NodeSetName;
|
|
FeNodeSet nodeSet = _mesh.NodeSets[nodeSetName];
|
|
for (int i = 0; i < nodeSet.Labels.Length; i++)
|
|
{
|
|
nodeIdCoor[nodeSet.Labels[i]] = _mesh.Nodes[nodeSet.Labels[i]].Coor;
|
|
}
|
|
}
|
|
else if (initialAngularVelocity.RegionType == RegionTypeEnum.ReferencePointName)
|
|
{
|
|
FeReferencePoint rp = _mesh.ReferencePoints[initialAngularVelocity.RegionName];
|
|
nodeIdCoor[referencePointsNodeIds[rp.Name][0]] = rp.Coor();
|
|
}
|
|
//
|
|
Dictionary<int, double[]> nodeIdVelocity;
|
|
initialAngularVelocity.GetTranslationalVelocities(nodeIdCoor, out nodeIdVelocity);
|
|
//
|
|
int count = 0;
|
|
int nodeId;
|
|
InitialTranslationalVelocity[] translationalVelocities = new InitialTranslationalVelocity[nodeIdVelocity.Count];
|
|
foreach (var entry in nodeIdVelocity)
|
|
{
|
|
nodeId = entry.Key;
|
|
// Is the node is on the axis
|
|
if (nodeId < 0)
|
|
{
|
|
// If the node is a translational node of the reference point, get a rotational node instead
|
|
foreach (var rpEntry in referencePointsNodeIds)
|
|
{
|
|
if (rpEntry.Value[0] == -nodeId)
|
|
{
|
|
nodeId = rpEntry.Value[1];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
//
|
|
translationalVelocities[count++] =
|
|
new InitialTranslationalVelocity("_iniTransVel_" + nodeId.ToString(), nodeId,
|
|
entry.Value[0], entry.Value[1], entry.Value[2],
|
|
initialAngularVelocity.TwoD, true);
|
|
}
|
|
//
|
|
return translationalVelocities;
|
|
}
|
|
// Defined fields
|
|
public DefinedTemperature[] GetNodalTemperaturesFromVariableDefinedTemperature(DefinedTemperature definedTemperature)
|
|
{
|
|
if (definedTemperature.Type == DefinedTemperatureTypeEnum.FromFile) return null;
|
|
if (definedTemperature.DistributionName == Distribution.DefaultDistributionName) return null;
|
|
// Node set
|
|
FeNodeSet nodeSet;
|
|
if (definedTemperature.RegionType == RegionTypeEnum.NodeSetName)
|
|
{
|
|
nodeSet = _mesh.NodeSets[definedTemperature.RegionName];
|
|
}
|
|
else if (definedTemperature.RegionType == RegionTypeEnum.SurfaceName)
|
|
{
|
|
FeSurface surface = _mesh.Surfaces[definedTemperature.RegionName];
|
|
nodeSet = _mesh.NodeSets[surface.NodeSetName];
|
|
}
|
|
else throw new NotSupportedException();
|
|
//
|
|
double[][] coor = new double[nodeSet.Labels.Length][];
|
|
// Parallel
|
|
Parallel.For(0, nodeSet.Labels.Length, i =>
|
|
{
|
|
int nodeId = nodeSet.Labels[i];
|
|
coor[i] = _mesh.Nodes[nodeId].Coor;
|
|
});
|
|
// Temperature
|
|
double[] temperatures;
|
|
definedTemperature.GetTemperaturesAndDistancesForPoints(this, coor, out _, out temperatures);
|
|
// Parallel
|
|
ConcurrentBag<DefinedTemperature> definedTemperatures = new ConcurrentBag<DefinedTemperature>();
|
|
Parallel.For(0, nodeSet.Labels.Length, i =>
|
|
{
|
|
int nodeId = nodeSet.Labels[i];
|
|
// Temperature definitions
|
|
DefinedTemperature dt = new DefinedTemperature("NodeID_" + nodeId.ToString(), nodeId, temperatures[i], true);
|
|
dt.AmplitudeName = definedTemperature.AmplitudeName;
|
|
definedTemperatures.Add(dt);
|
|
});
|
|
//
|
|
return definedTemperatures.ToArray();
|
|
}
|
|
// Parameters
|
|
public void UpdateNCalcParameters()
|
|
{
|
|
try
|
|
{
|
|
MyNCalc.ExistingParameters = new OrderedDictionary<string, object>("Parameters");
|
|
MyNCalc.ExistingParameters.AddRange(GetPropertyParameters());
|
|
foreach (var entry in _parameters) MyNCalc.ExistingParameters.Add(entry.Key, entry.Value.Value);
|
|
}
|
|
catch
|
|
{
|
|
MessageBoxes.ShowError("The parameters could not be evaluated. Check the parameter equations.");
|
|
}
|
|
}
|
|
public Dictionary<string, object> GetPropertyParameters()
|
|
{
|
|
double volume;
|
|
double area;
|
|
string prefix = "Geometry";
|
|
Dictionary<string, object> propParameters = new Dictionary<string, object>();
|
|
// Geometry
|
|
string volumePrefix = prefix + ".Volume";
|
|
propParameters.Add(volumePrefix, 0);
|
|
string areaPrefix = prefix + ".Area";
|
|
propParameters.Add(areaPrefix, 0);
|
|
//
|
|
volume = 0;
|
|
area = 0;
|
|
HashSet<string> compoundSubpartNames = new HashSet<string>();
|
|
foreach (var partEntry in _geometry.Parts)
|
|
{
|
|
if (partEntry.Value is CompoundGeometryPart cgp) compoundSubpartNames.UnionWith(cgp.SubPartNames);
|
|
}
|
|
foreach (var partEntry in _geometry.Parts)
|
|
{
|
|
if (partEntry.Value is CompoundGeometryPart cgp)
|
|
{
|
|
AddCompoundPartProperties(prefix, cgp, ref propParameters);
|
|
volume += (double)propParameters[prefix + "." + partEntry.Value.Name + ".Volume"];
|
|
area += (double)propParameters[prefix + "." + partEntry.Value.Name + ".Area"];
|
|
}
|
|
else if (!compoundSubpartNames.Contains(partEntry.Value.Name))
|
|
{
|
|
AddNonCompoundPartProperties(prefix, partEntry.Value, ref propParameters);
|
|
volume += (double)propParameters[prefix + "." + partEntry.Value.Name + ".Volume"];
|
|
area += (double)propParameters[prefix + "." + partEntry.Value.Name + ".Area"];
|
|
}
|
|
}
|
|
propParameters[volumePrefix] = volume;
|
|
propParameters[areaPrefix] = area;
|
|
// Mesh
|
|
prefix = "Mesh";
|
|
volumePrefix = prefix + ".Volume";
|
|
propParameters.Add(volumePrefix, 0);
|
|
areaPrefix = prefix + ".Area";
|
|
propParameters.Add(areaPrefix, 0);
|
|
//
|
|
volume = 0;
|
|
area = 0;
|
|
foreach (var partEntry in _mesh.Parts)
|
|
{
|
|
AddNonCompoundPartProperties(prefix, partEntry.Value, ref propParameters);
|
|
volume += (double)propParameters[prefix + "." + partEntry.Value.Name + ".Volume"];
|
|
area += (double)propParameters[prefix + "." + partEntry.Value.Name + ".Area"];
|
|
}
|
|
propParameters[volumePrefix] = volume;
|
|
propParameters[areaPrefix] = area;
|
|
//
|
|
return propParameters;
|
|
}
|
|
public string[] GetAllParameterNames()
|
|
{
|
|
List<string> parameterNames = new List<string>();
|
|
parameterNames.AddRange(_parameters.Keys);
|
|
parameterNames.AddRange(GetPropertyParameters().Keys);
|
|
return parameterNames.ToArray();
|
|
}
|
|
private void AddCompoundPartProperties(string prefix, CompoundGeometryPart part,
|
|
ref Dictionary<string, object> propParameters)
|
|
{
|
|
// Geometry
|
|
prefix += "." + part.Name;
|
|
string volumePrefix = prefix + ".Volume";
|
|
propParameters.Add(volumePrefix, 0);
|
|
string areaPrefix = prefix + ".Area";
|
|
propParameters.Add(areaPrefix, 0);
|
|
double volume = 0;
|
|
double area = 0;
|
|
foreach (var subPartName in part.SubPartNames)
|
|
{
|
|
AddNonCompoundPartProperties(prefix, _geometry.Parts[subPartName], ref propParameters);
|
|
volume += (double)propParameters[prefix + "." + subPartName + ".Volume"];
|
|
area += (double)propParameters[prefix + "." + subPartName + ".Area"];
|
|
}
|
|
propParameters[volumePrefix] = volume;
|
|
propParameters[areaPrefix] = area;
|
|
}
|
|
private void AddNonCompoundPartProperties(string prefix, BasePart part, ref Dictionary<string, object> propParameters)
|
|
{
|
|
bool solid = part.PartType == PartType.Solid || part.PartType == PartType.SolidAsShell;
|
|
if (part is CompoundGeometryPart)
|
|
throw new NotSupportedException();
|
|
else
|
|
{
|
|
VisualizationData vis = part.Visualization;
|
|
prefix += "." + part.Name;
|
|
// Volume
|
|
double volume = 0;
|
|
if (solid) volume = part.MassProperties.Volume;
|
|
propParameters.Add(prefix + ".Volume", volume);
|
|
// Area
|
|
string areaPrefix = prefix + ".Area";
|
|
propParameters.Add(areaPrefix, -1);
|
|
// Surfaces
|
|
double area = 0;
|
|
double perimeter;
|
|
string surfacePrefix;
|
|
for (int i = 0; i < vis.FaceCount; i++)
|
|
{
|
|
surfacePrefix = prefix + ".Surface-" + (i + 1);
|
|
propParameters.Add(surfacePrefix + ".Area", vis.FaceAreas[i]);
|
|
area += vis.FaceAreas[i];
|
|
perimeter = 0;
|
|
for (int j = 0; j < vis.FaceEdgeIds[i].Length; j++) perimeter += vis.EdgeLengths[vis.FaceEdgeIds[i][j]];
|
|
//propParameters.Add(surfacePrefix + ".Perimeter", perimeter);
|
|
}
|
|
//
|
|
propParameters[areaPrefix] = area;
|
|
// Edges
|
|
string edgePrefix;
|
|
for (int i = 0; i < vis.EdgeCount; i++)
|
|
{
|
|
edgePrefix = prefix + ".Edge-" + (i + 1);
|
|
//propParameters.Add(edgePrefix + ".Length", vis.EdgeLengths[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 3D - 2D
|
|
public void UpdateMeshPartsElementTypes(bool allowMixedModel)
|
|
{
|
|
Dictionary<Type, HashSet<Enum>> elementTypeEnums = _properties.ModelSpace.GetAvailableElementTypes(allowMixedModel);
|
|
if (_mesh != null) _mesh.UpdatePartsElementTypes(elementTypeEnums);
|
|
}
|
|
// Boundary displacement method
|
|
public FeModel PrepareBdmModel(Dictionary<int, double[]> deformations)
|
|
{
|
|
// Mesh
|
|
FeModel bdmModel = new FeModel("BDMmodel", _unitSystem);
|
|
bdmModel.Properties = _properties;
|
|
bdmModel.SetMesh(_mesh.DeepCopy());
|
|
// Materials
|
|
Material materialElastic = new Material("Elastic");
|
|
materialElastic.AddProperty(new Elastic(new double[][] { new double[] { 1000, 0, 0 } }));
|
|
bdmModel._materials.Add(materialElastic.Name, materialElastic);
|
|
// Sections
|
|
Section section;
|
|
foreach (var entry in _sections)
|
|
{
|
|
section = entry.Value.DeepClone();
|
|
section.MaterialName = materialElastic.Name;
|
|
bdmModel.Sections.Add(section.Name, section);
|
|
}
|
|
// Constraints
|
|
Constraint constraint;
|
|
foreach (var entry in _constraints)
|
|
{
|
|
constraint = entry.Value.DeepClone();
|
|
bdmModel.Constraints.Add(constraint.Name, constraint);
|
|
}
|
|
// Distributions
|
|
Distribution distribution;
|
|
foreach (var entry in _distributions)
|
|
{
|
|
distribution = entry.Value.DeepClone();
|
|
bdmModel.Distributions.Add(distribution.Name, distribution);
|
|
}
|
|
// Amplitudes
|
|
Amplitude amplitude;
|
|
foreach (var entry in _amplitudes)
|
|
{
|
|
amplitude = entry.Value.DeepClone();
|
|
bdmModel.Amplitudes.Add(amplitude.Name, amplitude);
|
|
}
|
|
// Steps
|
|
StaticStep staticStep = new StaticStep("Step-1");
|
|
bdmModel._stepCollection.AddStep(staticStep, false);
|
|
BoundaryDisplacementStep boundaryDisplacementStep = _stepCollection.GetBoundaryDisplacementStep();
|
|
if (boundaryDisplacementStep == null)
|
|
throw new CaeException("The boundary displacement step is missing.");
|
|
// Add existing boundary conditions
|
|
bool twoD = false;
|
|
BoundaryCondition bc;
|
|
foreach (var entry in boundaryDisplacementStep.BoundaryConditions)
|
|
{
|
|
bc = entry.Value.DeepClone();
|
|
staticStep.AddBoundaryCondition(bc);
|
|
twoD = bc.TwoD;
|
|
}
|
|
// Create BDM boundary conditions
|
|
string name;
|
|
double[] xyz;
|
|
FeNodeSet nodeSet;
|
|
DisplacementRotation displacementRotation;
|
|
//
|
|
if (deformations != null)
|
|
{
|
|
foreach (var entry in deformations)
|
|
{
|
|
// Node set
|
|
name = bdmModel.Mesh.NodeSets.GetNextNumberedKey("BDM", "_" + entry.Key);
|
|
nodeSet = new FeNodeSet(name, new int[] { entry.Key });
|
|
bdmModel.Mesh.NodeSets.Add(nodeSet.Name, nodeSet);
|
|
// Boundary condition
|
|
xyz = entry.Value;
|
|
name = staticStep.BoundaryConditions.GetNextNumberedKey("BDM-" + entry.Key);
|
|
displacementRotation = new DisplacementRotation(name, nodeSet.Name, RegionTypeEnum.NodeSetName, twoD,
|
|
false, 0);
|
|
if (xyz[0] != 0) displacementRotation.U1.SetEquationFromValue(xyz[0]);
|
|
if (xyz[1] != 0) displacementRotation.U2.SetEquationFromValue(xyz[1]);
|
|
if (xyz[2] != 0) displacementRotation.U3.SetEquationFromValue(xyz[2]);
|
|
staticStep.AddBoundaryCondition(displacementRotation);
|
|
}
|
|
}
|
|
//
|
|
return bdmModel;
|
|
}
|
|
// ISerialization
|
|
public void GetObjectData(SerializationInfo info, StreamingContext context)
|
|
{
|
|
// Using typeof() works also for null fields
|
|
info.AddValue("_name", Name, typeof(string));
|
|
info.AddValue("_geometry", _geometry, typeof(FeMesh));
|
|
info.AddValue("_mesh", _mesh, typeof(FeMesh));
|
|
info.AddValue("_parameters", _parameters, typeof(EquationParameterCollection));
|
|
info.AddValue("_materials", _materials, typeof(OrderedDictionary<string, Material>));
|
|
info.AddValue("_sections", _sections, typeof(OrderedDictionary<string, Section>));
|
|
info.AddValue("_constraints", _constraints, typeof(OrderedDictionary<string, Constraint>));
|
|
info.AddValue("_surfaceInteractions", _surfaceInteractions, typeof(OrderedDictionary<string, SurfaceInteraction>));
|
|
info.AddValue("_contactPairs", _contactPairs, typeof(OrderedDictionary<string, ContactPair>));
|
|
info.AddValue("_distributions", _distributions, typeof(OrderedDictionary<string, Distribution>));
|
|
info.AddValue("_amplitudes", _amplitudes, typeof(OrderedDictionary<string, Amplitude>));
|
|
info.AddValue("_initialConditions", _initialConditions, typeof(OrderedDictionary<string, InitialCondition>));
|
|
info.AddValue("_stepCollection", _stepCollection, typeof(StepCollection));
|
|
info.AddValue("_calculixUserKeywords", _calculixUserKeywords, typeof(OrderedDictionary<int[], Calculix.CalculixUserKeyword>));
|
|
info.AddValue("_properties", _properties, typeof(ModelProperties));
|
|
info.AddValue("_unitSystem", _unitSystem, typeof(UnitSystem));
|
|
info.AddValue("_hashName", _hashName, typeof(string));
|
|
}
|
|
|
|
}
|
|
}
|