Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/MGroup.AISolve.MSolve/IMSolveModelResponse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ public interface IMSolveModelResponse : IModelResponse
/// </summary>
/// <param name="parameterValues">A double array containing arbitrary parameters ordered as in <see cref="IModelResponse.GetModelResponse"/>.</param>
/// <returns>
/// A <see cref="IParentAnalyzer"/> instance for the solution of the <see cref="MGroup.MSolve.Discretization.Entities.Model"/>
/// made, as obtained from <see cref="ModelCreator"/>.
/// A <see cref="IParentAnalyzer"/> instance for the solution of the <see cref="MGroup.MSolve.Discretization.Entities.Model"/>,
/// as obtained from <see cref="ModelCreator"/>.
/// </returns>
IParentAnalyzer InitializeProblem(double[] parameterValues);

Expand All @@ -34,7 +34,7 @@ public interface IMSolveModelResponse : IModelResponse
/// </summary>
/// <param name="parameterValues">A double array containing arbitrary parameters ordered as in <see cref="IModelResponse.GetModelResponse"/>.</param>
/// <returns>A double array with the raw values of the solution of the linear system constructed by <see cref="InitializeProblem"/>.</returns>
/// <exception cref="InvalidOperationException"></exception>
/// <exception cref="InvalidOperationException">Thrown if this object was not initialized properly.</exception>
double[] GetModelResponse(double[] parameterValues)
{
if (ModelCreator == null)
Expand Down
19 changes: 11 additions & 8 deletions src/MGroup.AISolve.MSolve/IModelCreator.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
using MGroup.MSolve.Discretization.Entities;
using MGroup.MSolve.Discretization.Entities;

namespace MGroup.AISolve.MSolve
{
/// <summary>
/// Defines the generation of a <see cref="Model"/> with specific parameters.
/// Defines the generation of a MSolve model as <see cref="Model"/>, with specific parameters.
/// </summary>
public interface IModelCreator
{
/// <summary>
/// Generates a <see cref="Model"/> that takes into account the <paramref name="parameterValues"/> provided.
/// </summary>
/// <param name="parameterValues">A double array containing arbitrary parameters ordered as in <see cref="IModelResponse.GetModelResponse"/>.</param>
/// <returns>A <see cref="Model"/> instance made using the <paramref name="parameterValues"/> provided.</returns>
Model GetModel(double[] parameterValues);
/// <summary>
/// Generates a <see cref="Model"/> that takes into account the <paramref name="parameterValues"/> provided.
/// </summary>
/// <param name="parameterValues">
/// A double array containing arbitrary parameters ordered as in
/// <see cref="MGroup.AISolve.Core.IModelResponse.GetModelResponse"/>.
/// </param>
/// <returns>A <see cref="Model"/> instance made using the <paramref name="parameterValues"/> provided.</returns>
Model GetModel(double[] parameterValues);
}
}
30 changes: 29 additions & 1 deletion tests/MGroup.AISolve.MSolve.Tests/Elasticity3DModelCreator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,20 @@

namespace MGroup.AISolve.MSolve.Tests
{
/// <summary>
/// Creates MSolve models for 3D elasticity analysis, with structured meshes.
/// </summary>
internal class Elasticity3DModelCreator : IModelCreator
{
private const double cubeSide = 1.0;
private const int numElementsPerSide = 16; // must be even

public Model GetModel(double[] parameterValues)
/// <summary>
/// Creates an MSolve model with structured mesh and the provided elasticity modulus and external distributed load.
/// </summary>
/// <param name="parameterValues">The model's parameters include elasticity modulus and external distributed load.</param>
/// <returns>An MSolve model</returns>
public Model GetModel(double[] parameterValues)
{
// Mesh
double minX = 0, minY = 0, minZ = 0;
Expand Down Expand Up @@ -77,6 +85,12 @@ public Model GetModel(double[] parameterValues)
return model;
}

/// <summary>
/// Finds nodes of the model that satisfy the provided criteria.
/// </summary>
/// <param name="predicate">The criteria that nodes must satisfy.</param>
/// <param name="model">The MSolve model.</param>
/// <returns>A list of the target nodes.</returns>
public static List<Node> FindNodesWith(Func<Node, bool> predicate, Model model)
{
var result = new List<Node>();
Expand Down Expand Up @@ -109,6 +123,14 @@ private static bool IsNodeLoaded(Node node)
return isLoaded;
}

/// <summary>
/// Calculates the mean vertical (z-axis) displacement of a specific node across all its instances in the FEM model.
/// </summary>
/// <param name="monitorNode">The node of interest.</param>
/// <param name="algebraicModel">An object that manages all matrices and vectors of the physical model.</param>
/// <param name="solverLogger">The logger used during solution.</param>
/// <param name="solutionValues">The displacements obtained by solving the linear system.</param>
/// <returns>The target vertical displacement.</returns>
public static double GetMeanValueOfMonitoredNode(Node monitorNode, IAlgebraicModel algebraicModel, ISolverLogger solverLogger, double[] solutionValues)
{
//int numIterations = solverLogger.GetNumIterationsOfIterativeAlgorithm(0);
Expand All @@ -117,6 +139,12 @@ public static double GetMeanValueOfMonitoredNode(Node monitorNode, IAlgebraicMod
return algebraicModel.ExtractSingleValue(solutionVector, monitorNode, StructuralDof.TranslationZ);
}

/// <summary>
/// Selects a node of the physical model to monitor during multiple solutions, based on its geometry.
/// </summary>
/// <param name="model">The physical model in MSolve.</param>
/// <returns>The target node to be used for monitoring.</returns>
/// <exception cref="Exception">Thrown if a monitor node cannot be decided.</exception>
public static Node FindMonitoredNode(Model model)
{
if (numElementsPerSide % 2 == 1)
Expand Down
25 changes: 22 additions & 3 deletions tests/MGroup.AISolve.MSolve.Tests/StructuralTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using MGroup.AISolve.Core;
using MGroup.AISolve.Core;
using System;
using System.Collections.Generic;
using System.Diagnostics;
Expand All @@ -7,14 +7,21 @@

namespace MGroup.AISolve.MSolve.Tests
{
/// <summary>
/// Tests that combine all AI-Solve features.
/// </summary>
public class StructuralTests
{
private const int numAnalysesTotal = 300;
private const int numSolutionsForTraining = 50; // paper used 300
private const int numPrincipalComponents = 8;
private const int seed = 13;

[Fact]
/// <summary>
/// Solves a 3D elasticity example with AI-Solve. Specifically the PCG algorithm is used, where the POD-2G method is
/// employed as a preconditioner and a machine learning surrogate is trained for warm start.
/// </summary>
[Fact]
public static void Elasticity3DTest()
{
// Do NOT execute on Azure DevOps
Expand All @@ -37,6 +44,11 @@ public static void Elasticity3DTest()
$"Mean uTop={mean}");
}

/// <summary>
/// Generates a list of random value sets for the model parameters.
/// </summary>
/// <param name="numAnalysesTotal">The number of parameter sets (each set for one analysis) to generate.</param>
/// <returns>A list of random value sets</returns>
private static IList<double[]> GenerateParameterValues(int numAnalysesTotal)
{
double meanE = 2000; //MPa
Expand All @@ -56,13 +68,20 @@ private static IList<double[]> GenerateParameterValues(int numAnalysesTotal)
return samples;
}

/// <summary>
/// Generates random numbers following the normal distribution.
/// </summary>
/// <param name="count">How many numbers to generate.</param>
/// <param name="mean">The mean of the distribution.</param>
/// <param name="stddev">The standard deviation of the distribution.</param>
/// <param name="rng">A random number generator.</param>
/// <returns>A collection of random numbers.</returns>
private static IEnumerable<double> GenerateSamplesNormal(int count, double mean, double stddev, Random rng)
{
double[] samples = new double[count];
MathNet.Numerics.Distributions.Normal.Samples(rng, samples, mean, stddev);
return samples;
//return Normal.Samples(rng, mean, stddev);
}

}
}
32 changes: 24 additions & 8 deletions tests/MGroup.AISolve.MSolve.Tests/UniformMeshGenerator3D.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//TODO: abstract this in order to be used with points in various coordinate systems
//TODO: abstract this in order to be used with points in various coordinate systems
//TODO: perhaps the origin should be (0.0, 0.0) and the meshes could then be transformed. Abaqus does something similar with its
// meshed parts during assembly
namespace MGroup.AISolve.MSolve.Tests
Expand All @@ -22,7 +22,19 @@ public class UniformMeshGenerator3D<TNode> : IMeshGenerator<TNode> where TNode :
private readonly int cellsPerX, cellsPerY, cellsPerZ;
private readonly int verticesPerX, verticesPerY, verticesPerZ;

public UniformMeshGenerator3D(double minX, double minY, double minZ, double maxX, double maxY, double maxZ,
/// <summary>
/// Creates a new instance of <see cref="UniformMeshGenerator3D{TNode}"/> with the specified settings.
/// </summary>
/// <param name="minX">The minimum coordinate along axis X.</param>
/// <param name="minY">The minimum coordinate along axis Y.</param>
/// <param name="minZ">The minimum coordinate along axis Z.</param>
/// <param name="maxX">The maximum coordinate along axis X.</param>
/// <param name="maxY">The maximum coordinate along axis Y.</param>
/// <param name="maxZ">The maximum coordinate along axis Z.</param>
/// <param name="cellsPerX">The number of cells/elements along axis X.</param>
/// <param name="cellsPerY">The number of cells/elements along axis Y.</param>
/// <param name="cellsPerZ">The number of cells/elements along axis Z.</param>
public UniformMeshGenerator3D(double minX, double minY, double minZ, double maxX, double maxY, double maxZ,
int cellsPerX, int cellsPerY, int cellsPerZ)
{
this.minX = minX;
Expand All @@ -39,13 +51,17 @@ public UniformMeshGenerator3D(double minX, double minY, double minZ, double maxX
this.verticesPerZ = cellsPerZ + 1;
}

/// <summary>
/// If true, the node and element IDs will be 0-bases. If false, they will be 1-based.
/// </summary>
public bool StartIDsAt0 { get; set; } = true;

/// <summary>
/// Generates a uniform mesh with the dimensions and density defined in the constructor.
/// </summary>
/// <returns></returns>
public (IReadOnlyList<TNode> nodes, IReadOnlyList<CellConnectivity<TNode>> elements)
/// <summary>
/// Generates a uniform mesh with the dimensions and density defined in the constructor.
/// </summary>
/// <param name="createNode">Function to create a concrete implementation of <typeparamref name="TNode"/>.</param>
/// <returns>The nodes and elements of the mesh.</returns>
public (IReadOnlyList<TNode> nodes, IReadOnlyList<CellConnectivity<TNode>> elements)
CreateMesh(CreateNode<TNode> createNode)
{
TNode[] nodes = CreateNodes(createNode);
Expand Down Expand Up @@ -125,4 +141,4 @@ private static int[] Hexa8VerticesConventional(int verticesPerX, int verticesPer
return vertices;
}
}
}
}
23 changes: 22 additions & 1 deletion tests/MGroup.AISolve.MSolve.Tests/Utilities.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,22 @@
using System;
using System;
using System.Collections.Generic;

namespace MGroup.AISolve.MSolve.Tests
{
/// <summary>
/// Utility methods to check equality.
/// </summary>
public static class Utilities
{
/// <summary>
/// Checks if two lists of 1st order tensors have equal entries.
/// </summary>
/// <param name="tensors1">The first list of tensors to check.</param>
/// <param name="tensors2">The second list of tensors to check.</param>
/// <param name="tolerance">
/// Two values are equal if the absolute of their relative difference is less than this tolerance.
/// </param>
/// <returns>True if the tensors are equal. False, otherwise.</returns>
public static bool AreTensorsEqual(IReadOnlyList<double[]> tensors1, IReadOnlyList<double[]> tensors2, double tolerance)
{
if (tensors1.Count != tensors2.Count) return false;
Expand All @@ -19,6 +31,15 @@ public static bool AreTensorsEqual(IReadOnlyList<double[]> tensors1, IReadOnlyLi
return true;
}

/// <summary>
/// Checks if two scalar values are equal.
/// </summary>
/// <param name="value1">The first value to check.</param>
/// <param name="value2">The second value to check.</param>
/// <param name="tolerance">
/// Two values are equal if the absolute of their relative difference is less than this tolerance.
/// </param>
/// <returns>True if the tensors are equal. False, otherwise.</returns>
public static bool AreValuesEqual(double value1, double value2, double tolerance)
{
if (Math.Abs(value2) <= tolerance) // Can't divide with expected ~= 0.
Expand Down
Loading