diff --git a/composite_calculator/lib/calculators/laminate_plate_properties_calculator.dart b/composite_calculator/lib/calculators/laminate_plate_properties_calculator.dart new file mode 100644 index 0000000..802d4ba --- /dev/null +++ b/composite_calculator/lib/calculators/laminate_plate_properties_calculator.dart @@ -0,0 +1,158 @@ +import 'dart:math'; + +import 'package:composite_calculator/composite_calculator.dart'; +import 'package:composite_calculator/models/in-plane-properties.dart'; +import 'package:composite_calculator/utils/layup_parser.dart'; +import 'package:composite_calculator/utils/matrix_to_list_extension.dart'; +import 'package:linalg/linalg.dart'; +import 'package:linalg/matrix.dart'; + +import '../models/laminate_plate_properties_input.dart'; +import '../models/laminate_plate_properties_output.dart'; + +class LaminatePlatePropertiesCalculator { + static LaminatePlatePropertiesOutput calculate( + LaminatePlatePropertiesInput input) { + Matrix A = Matrix.fill(3, 3); + Matrix B = Matrix.fill(3, 3); + Matrix D = Matrix.fill(3, 3); + + double e1 = input.E1; + double e2 = input.E2; + double g12 = input.G12; + double nu12 = input.nu12; + double thickness = input.layerThickness; + String layupSequence = input.layupSequence; + List layups = LayupParser.parse(layupSequence) ?? []; + int nPly = layups.length; + + List bzi = []; + for (int i = 1; i <= nPly; i++) { + double bz = (-(nPly + 1) * thickness) / 2 + i * thickness; + bzi.add(bz); + } + for (int i = 0; i < nPly; i++) { + double layup = layups[i]; + + double angleRadian = layup * pi / 180; + double s = sin(angleRadian); + double c = cos(angleRadian); + + Matrix Sep = Matrix([ + [1 / e1, -nu12 / e1, 0], + [-nu12 / e1, 1 / e2, 0], + [0, 0, 1 / g12] + ]); + + Matrix Qep = Sep.inverse(); + + Matrix Rsigmae = Matrix([ + [c * c, s * s, -2 * s * c], + [s * s, c * c, 2 * s * c], + [s * c, -s * c, c * c - s * s] + ]); + + Matrix Qe = Rsigmae * Qep * Rsigmae.transpose(); + + A += Qe * thickness; + B += Qe * thickness * bzi[i]; + D += Qe * (thickness * bzi[i] * bzi[i] + pow(thickness, 3) / 12); + } + + LaminatePlatePropertiesOutput output = LaminatePlatePropertiesOutput( + A: A.toListOfLists(), + B: B.toListOfLists(), + D: D.toListOfLists(), + ); + + double h = nPly * thickness; + + Matrix Ses = A.inverse() * h; + Matrix Sesf = D.inverse() * (pow(h, 3) / 12); + + output.inPlaneProperties = InPlaneProperties( + analysisType: input.analysisType, + E1: 1 / Ses[0][0], + E2: 1 / Ses[1][1], + G12: 1 / Ses[2][2], + nu12: -1 / Ses[0][0] * Ses[0][1], + eta121: -1 / Ses[2][2] * Ses[0][2], + eta122: -1 / Ses[2][2] * Ses[1][2], + ); + + output.flexuralProperties = InPlaneProperties( + analysisType: input.analysisType, + E1: 1 / Sesf[0][0], + E2: 1 / Sesf[1][1], + G12: 1 / Sesf[2][2], + nu12: -1 / Sesf[0][0] * Sesf[0][1], + eta121: -1 / Sesf[2][2] * Sesf[0][2], + eta122: -1 / Sesf[2][2] * Sesf[1][2], + ); + + if (input.analysisType == AnalysisType.thermalElastic) { + double alpha11 = input.alpha11; + double alpha22 = input.alpha22; + double alpha12 = input.alpha12; + + Matrix temp_eff = Matrix.fill(3, 1); + Matrix temp_flex = Matrix.fill(3, 1); + + for (int i = 0; i < nPly; i++) { + double layup = layups[i]; + + double angleRadian = layup * pi / 180; + double s = sin(angleRadian); + double c = cos(angleRadian); + + Matrix Sep = Matrix([ + [1 / e1, -nu12 / e1, 0], + [-nu12 / e1, 1 / e2, 0], + [0, 0, 1 / g12] + ]); + + Matrix Qep = Sep.inverse(); + + Matrix Rsigmae = Matrix([ + [c * c, s * s, -2 * s * c], + [s * s, c * c, 2 * s * c], + [s * c, -s * c, c * c - s * s] + ]); + + Matrix Qe = Rsigmae * Qep * Rsigmae.transpose(); + + Matrix R_epsilon_e = Matrix([ + [c * c, s * s, -s * c], + [s * s, c * c, s * c], + [2 * s * c, -2 * s * c, c * c - s * s] + ]); + + Matrix cteVector = Matrix([ + [alpha11], + [alpha22], + [2 * alpha12] + ]); + + temp_eff += Qe * R_epsilon_e * cteVector * thickness; + temp_flex += Qe * + R_epsilon_e * + cteVector * + (thickness * bzi[i] * bzi[i] + + thickness * thickness * thickness / 12); + } + + Matrix cteVector_effective = A.inverse() * temp_eff; + Matrix cteVector_flexural = D.inverse() * temp_flex; + + output.inPlaneProperties.alpha11 = cteVector_effective[0][0]; + output.inPlaneProperties.alpha22 = cteVector_effective[1][0]; + output.inPlaneProperties.alpha12 = cteVector_effective[2][0]; + + output.flexuralProperties.alpha11 = cteVector_flexural[0][0]; + output.flexuralProperties.alpha22 = cteVector_flexural[1][0]; + output.flexuralProperties.alpha12 = cteVector_flexural[2][0]; + } + + return output; + } +} diff --git a/composite_calculator/lib/composite_calculator.dart b/composite_calculator/lib/composite_calculator.dart index 32f71e9..2e2ab8b 100644 --- a/composite_calculator/lib/composite_calculator.dart +++ b/composite_calculator/lib/composite_calculator.dart @@ -1,4 +1,12 @@ export 'models/analysis_type.dart'; + export 'models/lamina_engineering_constants_input.dart'; export 'models/lamina_engineering_constants_output.dart'; -export 'calculators/lamina_engineering_constants_calculator.dart'; \ No newline at end of file +export 'models/lamina_stress_strain_input.dart'; +export 'models/lamina_stress_strain_output.dart'; +export 'models/laminate_plate_properties_input.dart'; +export 'models/laminate_plate_properties_output.dart'; + +export 'calculators/lamina_engineering_constants_calculator.dart'; +export 'calculators/lamina_stress_strain_calculator.dart'; +export 'calculators/laminate_plate_properties_calculator.dart'; \ No newline at end of file diff --git a/composite_calculator/lib/models/in-plane-properties.dart b/composite_calculator/lib/models/in-plane-properties.dart new file mode 100644 index 0000000..6bd3f29 --- /dev/null +++ b/composite_calculator/lib/models/in-plane-properties.dart @@ -0,0 +1,46 @@ +import 'analysis_type.dart'; + +class InPlaneProperties { + AnalysisType analysisType; + double E1; + double E2; + double G12; + double nu12; + double eta121; + double eta122; + double alpha11; + double alpha22; + double alpha12; + + InPlaneProperties({ + this.analysisType = AnalysisType.elastic, + this.E1 = 0, + this.E2 = 0, + this.G12 = 0, + this.nu12 = 0, + this.eta121 = 0, + this.eta122 = 0, + this.alpha11 = 0, + this.alpha22 = 0, + this.alpha12 = 0, + }); + + Map toJson() { + Map result = { + 'E1': E1, + 'E2': E2, + 'G12': G12, + 'nu12': nu12, + 'eta121': eta121, + 'eta122': eta122, + }; + if (analysisType == AnalysisType.thermalElastic) { + result.addAll({ + 'alpha11': alpha11, + 'alpha22': alpha22, + 'alpha12': alpha12, + }); + } + return result; + } +} diff --git a/composite_calculator/lib/models/laminate_plate_properties_input.dart b/composite_calculator/lib/models/laminate_plate_properties_input.dart new file mode 100644 index 0000000..39c5994 --- /dev/null +++ b/composite_calculator/lib/models/laminate_plate_properties_input.dart @@ -0,0 +1,76 @@ +import 'analysis_type.dart'; + +class LaminatePlatePropertiesInput { + AnalysisType analysisType; + double E1; + double E2; + double G12; + double nu12; + String layupSequence; + double layerThickness; + + double alpha11; + double alpha22; + double alpha12; + + LaminatePlatePropertiesInput({ + this.analysisType = AnalysisType.elastic, + this.E1 = 0, + this.E2 = 0, + this.G12 = 0, + this.nu12 = 0, + this.layupSequence = "", + this.layerThickness = 0, + this.alpha11 = 0, + this.alpha22 = 0, + this.alpha12 = 0, + }); + + // Factory method to create an instance with default values + factory LaminatePlatePropertiesInput.withDefaults() { + return LaminatePlatePropertiesInput( + E1: 150000, + E2: 10000, + G12: 5000, + nu12: 0.3, + layupSequence: "[0/90/45/-45]s", + layerThickness: 0.125 + ); + } + + Map toJson() { + return { + 'analysis_type': analysisType.toJson(), + 'E1': E1, + 'E2': E2, + 'G12': G12, + 'nu12': nu12, + 'layup_sequence': layupSequence, + 'layer_thickness': layerThickness, + 'alpha11': alpha11, + 'alpha22': alpha22, + 'alpha12': alpha12, + }; + } + + // Factory method to create an instance from a JSON map + factory LaminatePlatePropertiesInput.fromJson(Map json) { + return LaminatePlatePropertiesInput( + analysisType: AnalysisType.values.firstWhere( + (e) => + e.toString() == + 'AnalysisType.' + (json['analysis_type'] ?? "elastic"), + orElse: () => AnalysisType.elastic, // Default value if not found + ), + E1: (json['E1'] ?? 0).toDouble(), + E2: (json['E2'] ?? 0).toDouble(), + G12: (json['G12'] ?? 0).toDouble(), + nu12: (json['nu12'] ?? 0).toDouble(), + layupSequence: (json['layup_sequence'] ?? "").toString(), + layerThickness: (json['layer_thickness'] ?? 0).toDouble(), + alpha11: (json['alpha11'] ?? 0).toDouble(), + alpha22: (json['alpha22'] ?? 0).toDouble(), + alpha12: (json['alpha12'] ?? 0).toDouble(), + ); + } +} \ No newline at end of file diff --git a/composite_calculator/lib/models/laminate_plate_properties_output.dart b/composite_calculator/lib/models/laminate_plate_properties_output.dart new file mode 100644 index 0000000..d272527 --- /dev/null +++ b/composite_calculator/lib/models/laminate_plate_properties_output.dart @@ -0,0 +1,29 @@ +import 'package:composite_calculator/models/in-plane-properties.dart'; + +class LaminatePlatePropertiesOutput { + List> A; + List> B; + List> D; + + InPlaneProperties inPlaneProperties; + InPlaneProperties flexuralProperties; + + LaminatePlatePropertiesOutput({ + this.A = const [], + this.B = const [], + this.D = const [], + InPlaneProperties? inPlaneProperties, + InPlaneProperties? flexuralProperties, + }) : inPlaneProperties = inPlaneProperties ?? InPlaneProperties(), + flexuralProperties = flexuralProperties ?? InPlaneProperties(); + + Map toJson() { + return { + 'A': A, + 'B': B, + 'D': D, + 'in-plane_properties': inPlaneProperties, + 'flexural_properties': flexuralProperties, + }; + } +} diff --git a/composite_calculator/lib/utils/layup_parser.dart b/composite_calculator/lib/utils/layup_parser.dart new file mode 100644 index 0000000..107894b --- /dev/null +++ b/composite_calculator/lib/utils/layup_parser.dart @@ -0,0 +1,83 @@ +class LayupParser { + static List? parse(String layupSequence) { + List layups = []; + String baseLayup = ""; + int rBefore = 1; + bool symmetry = false; + int rAfter = 1; + + if (layupSequence.split("]").length == 2 && layupSequence.split("]")[1] != "") { + baseLayup = layupSequence.split("]")[0].replaceAll("[", ""); + String msn = layupSequence.split("]")[1]; + if (msn.contains('s')) { + // symmetry + symmetry = true; + var r = msn.split('s'); + + if (r.length == 2) { + if (r[0] != "") { + int? rBeforeTemp = int.tryParse(r[0]); + if (rBeforeTemp != null) { + rBefore = rBeforeTemp; + } else { + return null; + } + } + if (r[1] != "") { + int? rAfterTemp = int.tryParse(r[1]); + if (rAfterTemp != null) { + rAfter = rAfterTemp; + } else { + return null; + } + } + } + } else { + // not symmetry + symmetry = false; + int? rBeforeTemp = int.tryParse(msn); + if (rBeforeTemp != null) { + rBefore = rBeforeTemp; + } else { + return null; + } + } + } else { + baseLayup = layupSequence.replaceAll("[", "").replaceAll("]", ''); + } + + for (String angleString in baseLayup.split('/')) { + double? angle = double.tryParse(angleString); + if (angle == null) { + return null; + } + layups!.add(angle); + } + + var layupsTemp = [...layups!]; + + for (var i = 1; i < rBefore; i++) { + for (var layup in layupsTemp) { + layups!.add(layup); + } + } + + layupsTemp = [...layups!]; + + if (symmetry) { + var layupsTempReversed = layupsTemp.reversed; + for (var layup in layupsTempReversed) { + layups!.add(layup); + } + } + + layupsTemp = [...layups!]; + + for (var i = 1; i < rAfter; i++) { + for (var layup in layupsTemp) { + layups!.add(layup); + } + } + return layups; + } +} \ No newline at end of file diff --git a/composite_calculator/test/laminate_plate_properties_calculator_test.dart b/composite_calculator/test/laminate_plate_properties_calculator_test.dart new file mode 100644 index 0000000..0706ecf --- /dev/null +++ b/composite_calculator/test/laminate_plate_properties_calculator_test.dart @@ -0,0 +1,102 @@ +import 'package:composite_calculator/calculators/lamina_engineering_constants_calculator.dart'; +import 'package:composite_calculator/calculators/laminate_plate_properties_calculator.dart'; +import 'package:composite_calculator/models/lamina_engineering_constants_input.dart'; +import 'package:composite_calculator/models/laminate_plate_properties_input.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group('LaminatePlatePropertiesCalculator Tests', () { + test('Default input test case', () { + // Arrange: Create input data with default values + var input = LaminatePlatePropertiesInput.withDefaults(); + + // Act: Calculate the output using the calculator + var output = LaminatePlatePropertiesCalculator.calculate(input); + + // Assert: + List> expectedA = [ + [63616.70020120725, 19884.305835010055, 2.2638717237759177e-14], + [19884.305835010055, 63616.70020120723, 1.9648901387141773e-12], + [2.263871723775917e-14, 1.9648901387141773e-12, 21866.19718309859] + ]; + + List> expectedB = [ + [0.0, -1.1368683772161603e-13, 7.074599136799743e-15], + [ + -1.1368683772161603e-13, + -3.410605131648481e-13, + -1.2493627684232382e-13 + ], + [7.074599136799741e-15, -1.1249439120707794e-14, 0.0] + ]; + + List> expectedD = [ + [8006.057176391683, 602.8881623071763, 275.08802816901414], + [602.8881623071763, 4705.000838363513, 275.08802816901425], + [275.08802816901414, 275.0880281690143, 768.0457746478874] + ]; + + for (int i = 0; i < output.A.length; i++) { + for (int j = 0; j < output.A[i].length; j++) { + expect(output.A[i][j], closeTo(expectedA[i][j], 1e-3)); + } + } + for (int i = 0; i < output.B.length; i++) { + for (int j = 0; j < output.B[i].length; j++) { + expect(output.B[i][j], closeTo(expectedB[i][j], 1e-3)); + } + } + for (int i = 0; i < output.D.length; i++) { + for (int j = 0; j < output.D[i].length; j++) { + expect(output.D[i][j], closeTo(expectedD[i][j], 1e-3)); + } + } + + expect(output.inPlaneProperties.E1, closeTo(57401.57717078148, 1e-3)); + expect(output.inPlaneProperties.E2, closeTo(57401.57717078147, 1e-3)); + expect(output.inPlaneProperties.G12, closeTo(21866.19718309859, 1e-3)); + expect(output.inPlaneProperties.nu12, closeTo(0.312564244484858, 1e-5)); + expect(output.inPlaneProperties.eta121, + closeTo(-1.030486815205974e-17, 1e-5)); + expect(output.inPlaneProperties.eta122, + closeTo(3.410732216189378e-17, 1e-5)); + + expect(output.flexuralProperties.E1, closeTo(94227.69205595773, 1e-3)); + expect(output.flexuralProperties.E2, closeTo(54891.6513180624, 1e-3)); + expect(output.flexuralProperties.G12, closeTo(8936.487112074954, 1e-3)); + expect( + output.flexuralProperties.nu12, closeTo(0.10948959541430607, 1e-5)); + expect( + output.flexuralProperties.eta121, closeTo(0.03024905861937437, 1e-5)); + expect(output.flexuralProperties.eta122, + closeTo(0.054591112229385966, 1e-5)); + }); + + test('Custom input test case', () { + // Arrange: Create custom input data + var input = LaminatePlatePropertiesInput( + E1: 200000, + E2: 10000, + G12: 5000, + nu12: 0.3, + layupSequence: "[0/90/45/-45]s", + layerThickness: 0.125); + + // Act: Calculate the output using the calculator + var output = LaminatePlatePropertiesCalculator.calculate(input); + + // Assert: + List> expectedA = [ + [82359.36715218483, 26128.8297338021, 2.271993528051501e-14], + [26128.8297338021, 82359.3671521848, 3.2571113306334796e-12], + [2.2719935280515002e-14, 3.2571113306334796e-12, 28115.268709191358] + ]; + + for (int i = 0; i < output.A.length; i++) { + for (int j = 0; j < output.A[i].length; j++) { + expect(output.A[i][j], closeTo(expectedA[i][j], 1e-3)); + } + } + }); + }); +} diff --git a/data/lib/repositories/function_tools_repository_imp.dart b/data/lib/repositories/function_tools_repository_imp.dart index f70a939..91d00eb 100644 --- a/data/lib/repositories/function_tools_repository_imp.dart +++ b/data/lib/repositories/function_tools_repository_imp.dart @@ -91,10 +91,39 @@ class FunctionToolsRepositoryImp extends FunctionToolsRepository { "gamma12": {"type": "number", "default": 0} } }); + FunctionTool calculateLaminatePlatePropertiesTool = FunctionTool( + name: "calculate_laminate_plate_properties", + description: + "Calculates the laminate plate properties. $commonDescription", + parameters: { + "type": "object", + "required": [ + "E1", + "E2", + "G12", + "nu12", + "layup_sequence", + "layer_thickness", + ], + "properties": { + "E1": {"type": "number", "default": 150000, "exclusiveMinimum": 0}, + "E2": {"type": "number", "default": 10000, "exclusiveMinimum": 0}, + "G12": {"type": "number", "default": 5000, "exclusiveMinimum": 0}, + "nu12": { + "type": "number", + "default": 0.3, + "maximum": 0.5, + "minimum": -1 + }, + "layup_sequence": {"type": "string", "default": "[0/90/45/-45]s"}, + "layer_thickness": {"type": "number", "default": 0.125, "exclusiveMinimum": 0} + } + }); return [ calculateLaminaEngineeringConstantsTool, calculateLaminaStrainTool, - calculateLaminaStressTool + calculateLaminaStressTool, + calculateLaminatePlatePropertiesTool ]; } } diff --git a/lib/presentation/chat/viewModels/chat_view_model.dart b/lib/presentation/chat/viewModels/chat_view_model.dart index 54e9cd9..59ba5e6 100644 --- a/lib/presentation/chat/viewModels/chat_view_model.dart +++ b/lib/presentation/chat/viewModels/chat_view_model.dart @@ -2,10 +2,14 @@ import 'dart:convert'; import 'package:composite_calculator/calculators/lamina_engineering_constants_calculator.dart'; import 'package:composite_calculator/calculators/lamina_stress_strain_calculator.dart'; +import 'package:composite_calculator/calculators/laminate_plate_properties_calculator.dart'; + import 'package:composite_calculator/models/lamina_engineering_constants_input.dart'; import 'package:composite_calculator/models/lamina_engineering_constants_output.dart'; import 'package:composite_calculator/models/lamina_stress_strain_input.dart'; import 'package:composite_calculator/models/lamina_stress_strain_output.dart'; +import 'package:composite_calculator/models/laminate_plate_properties_input.dart'; +import 'package:composite_calculator/models/laminate_plate_properties_output.dart'; import 'package:composite_calculator/models/tensor_type.dart'; import 'package:domain/domain.dart'; import 'package:domain/entities/chat_session.dart'; @@ -39,6 +43,7 @@ class ChatViewModel extends ChangeNotifier { "Calculate lamina engineering constants", "Calculate lamina strain", "Calculate lamina stress", + "Calculate laminate plate properties", "What is the upper bound of Young's modulus for composites?", // "How to use SwiftComp?", // "Give me some math equations.", @@ -167,6 +172,12 @@ class ChatViewModel extends ChangeNotifier { LaminaStressStrainOutput output = LaminaStressStrainCalculator.calculate(input); outputString = output.toJson().toString(); + } else if (functionName == "calculate_laminate_plate_properties") { + LaminatePlatePropertiesInput input = + LaminatePlatePropertiesInput.fromJson(argumentsJson); + LaminatePlatePropertiesOutput output = + LaminatePlatePropertiesCalculator.calculate(input); + outputString = output.toJson().toString(); } _chatSessionUseCase.addMessageToSession(selectedSession!, Message(role: "tool", content: outputString, tool_call_id: tool.id)); diff --git a/lib/presentation/tools/model/layup_sequence_model.dart b/lib/presentation/tools/model/layup_sequence_model.dart index 0bd9d2c..6b798bc 100644 --- a/lib/presentation/tools/model/layup_sequence_model.dart +++ b/lib/presentation/tools/model/layup_sequence_model.dart @@ -2,6 +2,7 @@ import 'package:flutter/foundation.dart'; class LayupSequence { List? layups; + String stringValue = ""; // set 方法 set value(String value) { diff --git a/lib/presentation/tools/model/validate.dart b/lib/presentation/tools/model/validate.dart index f98ab1f..8130352 100644 --- a/lib/presentation/tools/model/validate.dart +++ b/lib/presentation/tools/model/validate.dart @@ -8,6 +8,14 @@ validateModulus(double? value) { } } +validateCTEs(double? value) { + if (value == null) { + return "Not a number"; + } else { + return null; + } +} + validateIsotropicPoissonRatio(double? value) { if (value == null) { return "Not a number"; diff --git a/lib/presentation/tools/page/UDFRC_rules_of_mixture_page.dart b/lib/presentation/tools/page/UDFRC_rules_of_mixture_page.dart index 18d7fea..6c5cb3d 100644 --- a/lib/presentation/tools/page/UDFRC_rules_of_mixture_page.dart +++ b/lib/presentation/tools/page/UDFRC_rules_of_mixture_page.dart @@ -72,7 +72,7 @@ class _RulesOfMixturePageState extends State { crossAxisSpacing: 12, itemBuilder: (BuildContext context, int index) { return [ - AnalysisType(callback: (type) { + AnalysisTypeRow(callback: (type) { isElastic = type == "Elastic"; }), TransverselyIsotropicRow( diff --git a/lib/presentation/tools/page/lamina_engineering_constants_page.dart b/lib/presentation/tools/page/lamina_engineering_constants_page.dart index 6191d30..0938197 100644 --- a/lib/presentation/tools/page/lamina_engineering_constants_page.dart +++ b/lib/presentation/tools/page/lamina_engineering_constants_page.dart @@ -60,7 +60,7 @@ class _LaminaEngineeringConstantsPageState extends State { crossAxisSpacing: 12, itemBuilder: (BuildContext context, int index) { return [ - AnalysisType(callback: (analysisType) { + AnalysisTypeRow(callback: (analysisType) { isElastic = analysisType == "Elastic"; }), LaminaContantsRow( diff --git a/lib/presentation/tools/page/laminate_3d_properties_page.dart b/lib/presentation/tools/page/laminate_3d_properties_page.dart index 79db7e3..fdd860d 100644 --- a/lib/presentation/tools/page/laminate_3d_properties_page.dart +++ b/lib/presentation/tools/page/laminate_3d_properties_page.dart @@ -76,7 +76,7 @@ class _Laminate3DPropertiesPageState extends State { crossAxisSpacing: 12, itemBuilder: (BuildContext context, int index) { return [ - AnalysisType(callback: (type) { + AnalysisTypeRow(callback: (type) { isElastic = type == "Elastic"; }), LaminaContantsRow( diff --git a/lib/presentation/tools/page/laminate_plate_properties_page.dart b/lib/presentation/tools/page/laminate_plate_properties_page.dart index 9da2a6d..353a1a9 100644 --- a/lib/presentation/tools/page/laminate_plate_properties_page.dart +++ b/lib/presentation/tools/page/laminate_plate_properties_page.dart @@ -1,10 +1,7 @@ -import 'dart:math'; - +import 'package:composite_calculator/composite_calculator.dart'; import 'package:flutter/material.dart'; import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; -import 'package:linalg/linalg.dart'; import 'package:swiftcomp/generated/l10n.dart'; -import 'package:swiftcomp/presentation/tools/model/in_plane_properties_model.dart'; import 'package:swiftcomp/presentation/tools/model/layer_thickness.dart'; import 'package:swiftcomp/presentation/tools/model/layup_sequence_model.dart'; import 'package:swiftcomp/presentation/tools/model/material_model.dart'; @@ -18,7 +15,6 @@ import 'package:swiftcomp/presentation/tools/widget/layup_sequence_row.dart'; import '../../tools/model/thermal_model.dart'; import '../widget/transversely_thermal_constants_row.dart'; -import 'package:vector_math/vector_math.dart' as VMath; class LaminatePlatePropertiesPage extends StatefulWidget { const LaminatePlatePropertiesPage({Key? key}) : super(key: key); @@ -75,7 +71,7 @@ class _LaminatePlatePropertiesPageState crossAxisSpacing: 12, itemBuilder: (BuildContext context, int index) { return [ - AnalysisType(callback: (type) { + AnalysisTypeRow(callback: (type) { isElastic = type == "Elastic"; }), LaminaContantsRow( @@ -110,140 +106,37 @@ class _LaminatePlatePropertiesPageState if (!isElastic && !transverselyIsotropicCTE.isValid()) { return; } - Matrix A = Matrix.fill(3, 3); - Matrix B = Matrix.fill(3, 3); - Matrix D = Matrix.fill(3, 3); - double thickness = layerThickness.value!; - int nPly = layupSequence.layups!.length; - - List bzi = []; - for (int i = 1; i <= nPly; i++) { - double bz = (-(nPly + 1) * thickness) / 2 + i * thickness; - bzi.add(bz); - } double e1 = transverselyIsotropicMaterial.e1!; double e2 = transverselyIsotropicMaterial.e2!; double g12 = transverselyIsotropicMaterial.g12!; double nu12 = transverselyIsotropicMaterial.nu12!; + double thickness = layerThickness.value!; - for (int i = 0; i < nPly; i++) { - double layup = layupSequence.layups![i]; - - double angleRadian = layup * pi / 180; - double s = sin(angleRadian); - double c = cos(angleRadian); - - Matrix Sep = Matrix([ - [1 / e1, -nu12 / e1, 0], - [-nu12 / e1, 1 / e2, 0], - [0, 0, 1 / g12] - ]); - - Matrix Qep = Sep.inverse(); - - Matrix Rsigmae = Matrix([ - [c * c, s * s, -2 * s * c], - [s * s, c * c, 2 * s * c], - [s * c, -s * c, c * c - s * s] - ]); - - Matrix Qe = Rsigmae * Qep * Rsigmae.transpose(); - - A += Qe * thickness; - B += Qe * thickness * bzi[i]; - D += Qe * (thickness * bzi[i] * bzi[i] + pow(thickness, 3) / 12); - } - - double h = nPly * thickness; - - Matrix Ses = A.inverse() * h; - Matrix Sesf = D.inverse() * (pow(h, 3) / 12); - - InPlanePropertiesModel inPlanePropertiesModel = InPlanePropertiesModel(); - inPlanePropertiesModel.e1 = 1 / Ses[0][0]; - inPlanePropertiesModel.e2 = 1 / Ses[1][1]; - inPlanePropertiesModel.g12 = 1 / Ses[2][2]; - inPlanePropertiesModel.nu12 = -1 / Ses[0][0] * Ses[0][1]; - inPlanePropertiesModel.eta121 = -1 / Ses[2][2] * Ses[0][2]; - inPlanePropertiesModel.eta122 = -1 / Ses[2][2] * Ses[1][2]; - - InPlanePropertiesModel flexuralPropertiesModel = InPlanePropertiesModel(); - flexuralPropertiesModel.e1 = 1 / Sesf[0][0]; - flexuralPropertiesModel.e2 = 1 / Sesf[1][1]; - flexuralPropertiesModel.g12 = 1 / Sesf[2][2]; - flexuralPropertiesModel.nu12 = -1 / Sesf[0][0] * Sesf[0][1]; - flexuralPropertiesModel.eta121 = -1 / Sesf[2][2] * Sesf[0][2]; - flexuralPropertiesModel.eta122 = -1 / Sesf[2][2] * Sesf[1][2]; + LaminatePlatePropertiesInput input = LaminatePlatePropertiesInput( + analysisType: + isElastic ? AnalysisType.elastic : AnalysisType.thermalElastic, + E1: e1, + E2: e2, + G12: g12, + nu12: nu12, + layupSequence: layupSequence.stringValue, + layerThickness: thickness); if (!isElastic) { - double alpha11 = transverselyIsotropicCTE.alpha11!; - double alpha22 = transverselyIsotropicCTE.alpha22!; - double alpha12 = transverselyIsotropicCTE.alpha12!; - - Matrix temp_eff = Matrix.fill(3, 1); - Matrix temp_flex = Matrix.fill(3, 1); - - for (int i = 0; i < nPly; i++) { - double layup = layupSequence.layups![i]; - - double angleRadian = layup * pi / 180; - double s = sin(angleRadian); - double c = cos(angleRadian); - - Matrix Sep = Matrix([ - [1 / e1, -nu12 / e1, 0], - [-nu12 / e1, 1 / e2, 0], - [0, 0, 1 / g12] - ]); - - Matrix Qep = Sep.inverse(); - - Matrix Rsigmae = Matrix([ - [c * c, s * s, -2 * s * c], - [s * s, c * c, 2 * s * c], - [s * c, -s * c, c * c - s * s] - ]); - - Matrix Qe = Rsigmae * Qep * Rsigmae.transpose(); - - Matrix R_epsilon_e = Matrix([ - [c * c, s * s, -s * c], - [s * s, c * c, s * c], - [2 * s * c, -2 * s * c, c * c - s * s] - ]); - - Matrix cteVector = Matrix([ - [alpha11], - [alpha22], - [2 * alpha12] - ]); - - temp_eff += Qe * R_epsilon_e * cteVector * thickness; - temp_flex += Qe * R_epsilon_e * cteVector * (thickness * bzi[i] * bzi[i] + thickness * thickness * thickness / 12); - } - - Matrix cteVector_effective = A.inverse() * temp_eff; - Matrix cteVector_flexural = D.inverse() * temp_flex; - - inPlanePropertiesModel.alpha11 = cteVector_effective[0][0]; - inPlanePropertiesModel.alpha22 = cteVector_effective[1][0]; - inPlanePropertiesModel.alpha12 = cteVector_effective[2][0]; - - flexuralPropertiesModel.alpha11 = cteVector_flexural[0][0]; - flexuralPropertiesModel.alpha22 = cteVector_flexural[1][0]; - flexuralPropertiesModel.alpha12 = cteVector_flexural[2][0]; + input.alpha11 = transverselyIsotropicCTE.alpha11!; + input.alpha22 = transverselyIsotropicCTE.alpha22!; + input.alpha12 = transverselyIsotropicCTE.alpha12!; } + LaminatePlatePropertiesOutput output = + LaminatePlatePropertiesCalculator.calculate(input); + Navigator.push( context, MaterialPageRoute( builder: (context) => LaminatePlatePropertiesResultPage( - A: A, - B: B, - D: D, - inPlanePropertiesModel: inPlanePropertiesModel, - flexuralPropertiesModel: flexuralPropertiesModel, + output: output, ))); } } diff --git a/lib/presentation/tools/page/laminate_plate_properties_result_page.dart b/lib/presentation/tools/page/laminate_plate_properties_result_page.dart index 7984a04..0d72f7d 100644 --- a/lib/presentation/tools/page/laminate_plate_properties_result_page.dart +++ b/lib/presentation/tools/page/laminate_plate_properties_result_page.dart @@ -1,5 +1,7 @@ import 'dart:math'; +import 'package:composite_calculator/composite_calculator.dart'; +import 'package:composite_calculator/models/in-plane-properties.dart'; import 'package:flutter/material.dart'; import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; import 'package:linalg/matrix.dart'; @@ -11,19 +13,9 @@ import 'package:swiftcomp/presentation/more/tool_setting_page.dart'; import 'package:swiftcomp/util/NumberPrecisionHelper.dart'; class LaminatePlatePropertiesResultPage extends StatefulWidget { - final Matrix A; - final Matrix B; - final Matrix D; - final InPlanePropertiesModel inPlanePropertiesModel; - final InPlanePropertiesModel flexuralPropertiesModel; + final LaminatePlatePropertiesOutput output; - const LaminatePlatePropertiesResultPage( - {Key? key, - required this.A, - required this.B, - required this.D, - required this.inPlanePropertiesModel, - required this.flexuralPropertiesModel}) + const LaminatePlatePropertiesResultPage({Key? key, required this.output}) : super(key: key); @override @@ -31,7 +23,8 @@ class LaminatePlatePropertiesResultPage extends StatefulWidget { _LaminatePlatePropertiesResultPageState(); } -class _LaminatePlatePropertiesResultPageState extends State { +class _LaminatePlatePropertiesResultPageState + extends State { @override Widget build(BuildContext context) { FocusManager.instance.primaryFocus?.unfocus(); @@ -39,14 +32,17 @@ class _LaminatePlatePropertiesResultPageState extends State Navigator.of(context).pop(), ), actions: [ IconButton( onPressed: () { Navigator.push( - context, MaterialPageRoute(builder: (context) => const ToolSettingPage())); + context, + MaterialPageRoute( + builder: (context) => const ToolSettingPage())); }, icon: const Icon(Icons.settings_rounded), ), @@ -58,33 +54,35 @@ class _LaminatePlatePropertiesResultPageState extends State - StaggeredTile.fit(MediaQuery.of(context).size.width > 600 ? 4 : 8), + staggeredTileBuilder: (int index) => StaggeredTile.fit( + MediaQuery.of(context).size.width > 600 ? 4 : 8), mainAxisSpacing: 12, crossAxisSpacing: 12, itemBuilder: (BuildContext context, int index) { return [ Result3By3Matrix( - matrix: widget.A, title: "A Matrix", + matrixList: widget.output.A, ), Result3By3Matrix( - matrix: widget.B, title: "B Matrix", + matrixList: widget.output.B, ), Result3By3Matrix( - matrix: widget.D, title: "D Matrix", + matrixList: widget.output.D, ), InPlanePropertiesWidget( title: "In-Plane Properties", - explain: "In-Plane properties are only valid for symmetric laminates only.", - inPlanePropertiesModel: widget.inPlanePropertiesModel, + explain: + "In-Plane properties are only valid for symmetric laminates only.", + inPlaneProperties: widget.output.inPlaneProperties, ), InPlanePropertiesWidget( title: "Flexural Properties", - explain: "Flexural properties are only valid for symmetric laminates only.", - inPlanePropertiesModel: widget.flexuralPropertiesModel, + explain: + "Flexural properties are only valid for symmetric laminates only.", + inPlaneProperties: widget.output.flexuralProperties, ) ][index]; }), @@ -95,16 +93,21 @@ class _LaminatePlatePropertiesResultPageState extends State(builder: (context, precs, child) { return SizedBox( height: 40, - child: Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + child: + Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( title, style: Theme.of(context).textTheme.titleMedium, @@ -121,13 +124,14 @@ class InPlanePropertiesWidget extends StatelessWidget { String getValue(double? value, int precision) { String valueString = ""; if (value != null) { - valueString = value == 0 ? "0" : value.toStringAsExponential(precision).toString(); + valueString = + value == 0 ? "0" : value.toStringAsExponential(precision).toString(); } return valueString; } double calculateHeight() { - if (inPlanePropertiesModel.alpha11 != null) { + if (inPlaneProperties.analysisType == AnalysisType.thermalElastic) { return 40 * 9 + 20; } else { return 40 * 6 + 20; @@ -136,6 +140,31 @@ class InPlanePropertiesWidget extends StatelessWidget { @override Widget build(BuildContext context) { + List list = [ + _propertyRow(context, "E1", inPlaneProperties.E1), + const Divider(height: 1), + _propertyRow(context, "E2", inPlaneProperties.E2), + const Divider(height: 1), + _propertyRow(context, "G12", inPlaneProperties.G12), + const Divider(height: 1), + _propertyRow(context, "ν12", inPlaneProperties.nu12), + const Divider(height: 1), + _propertyRow(context, "η12,1", inPlaneProperties.eta121), + const Divider(height: 1), + _propertyRow(context, "η12,2", inPlaneProperties.eta122), + ]; + + if (inPlaneProperties.analysisType == AnalysisType.thermalElastic) { + list.addAll([ + const Divider(height: 1), + _propertyRow(context, "ɑ11", inPlaneProperties.alpha11), + const Divider(height: 1), + _propertyRow(context, "ɑ22", inPlaneProperties.alpha22), + const Divider(height: 1), + _propertyRow(context, "ɑ12", inPlaneProperties.alpha12), + ]); + } + return Card( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12.0), @@ -155,12 +184,15 @@ class InPlanePropertiesWidget extends StatelessWidget { Dialog dialog = Dialog( insetPadding: EdgeInsets.fromLTRB(20, 20, 20, 20), shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(12.0)), //this right here + borderRadius: BorderRadius.circular(12.0)), + //this right here child: Container( padding: EdgeInsets.fromLTRB(12, 20, 12, 20), child: Text(explain!)), ); - showDialog(context: context, builder: (BuildContext context) => dialog); + showDialog( + context: context, + builder: (BuildContext context) => dialog); }, icon: Icon( Icons.help_outline_rounded, @@ -170,37 +202,12 @@ class InPlanePropertiesWidget extends StatelessWidget { ], ), ), - Container( padding: EdgeInsets.fromLTRB(20, 0, 20, 20), height: calculateHeight(), child: ListView( physics: const NeverScrollableScrollPhysics(), - children: [ - _propertyRow(context, "E1", inPlanePropertiesModel.e1), - const Divider(height: 1), - _propertyRow(context, "E2", inPlanePropertiesModel.e2), - const Divider(height: 1), - _propertyRow(context, "G12", inPlanePropertiesModel.g12), - const Divider(height: 1), - _propertyRow(context, "ν12", inPlanePropertiesModel.nu12), - const Divider(height: 1), - _propertyRow(context, "η12,1", inPlanePropertiesModel.eta121), - const Divider(height: 1), - _propertyRow(context, "η12,2", inPlanePropertiesModel.eta122), - if (inPlanePropertiesModel.alpha11 != null) - const Divider(height: 1), - if (inPlanePropertiesModel.alpha11 != null) - _propertyRow(context, "ɑ11", inPlanePropertiesModel.alpha11), - if (inPlanePropertiesModel.alpha22 != null) - const Divider(height: 1), - if (inPlanePropertiesModel.alpha22 != null) - _propertyRow(context, "ɑ22", inPlanePropertiesModel.alpha22), - if (inPlanePropertiesModel.alpha12 != null) - const Divider(height: 1), - if (inPlanePropertiesModel.alpha12 != null) - _propertyRow(context, "ɑ12", inPlanePropertiesModel.alpha12), - ], + children: list, ), ), ], diff --git a/lib/presentation/tools/widget/analysis_type_row.dart b/lib/presentation/tools/widget/analysis_type_row.dart index 4fb87c3..00792ae 100644 --- a/lib/presentation/tools/widget/analysis_type_row.dart +++ b/lib/presentation/tools/widget/analysis_type_row.dart @@ -1,15 +1,15 @@ import 'package:flutter/material.dart'; -class AnalysisType extends StatefulWidget { +class AnalysisTypeRow extends StatefulWidget { final Function(String) callback; - const AnalysisType({Key? key, required this.callback}) : super(key: key); + const AnalysisTypeRow({Key? key, required this.callback}) : super(key: key); @override - State createState() => _AnalysisTypeState(); + State createState() => _AnalysisTypeRowState(); } -class _AnalysisTypeState extends State { +class _AnalysisTypeRowState extends State { String analysisType = "Elastic"; @override diff --git a/lib/presentation/tools/widget/layup_sequence_row.dart b/lib/presentation/tools/widget/layup_sequence_row.dart index e5d9397..e9bccd7 100644 --- a/lib/presentation/tools/widget/layup_sequence_row.dart +++ b/lib/presentation/tools/widget/layup_sequence_row.dart @@ -96,6 +96,7 @@ class _LayupSequenceRowState extends State { onChanged: (value) { setState(() { widget.layupSequence.value = value; + widget.layupSequence.stringValue = value; }); }, ), @@ -103,26 +104,35 @@ class _LayupSequenceRowState extends State { Row( children: [ // ElevatedButton(onPressed: onPressed, child: child) - ElevatedButton(onPressed: () { - final updatedText = _textEditingController.text + '/'; - setState(() { - _textEditingController.text = updatedText; - }); - }, child: Text("/")), + ElevatedButton( + onPressed: () { + final updatedText = + _textEditingController.text + '/'; + setState(() { + _textEditingController.text = updatedText; + }); + }, + child: Text("/")), const SizedBox(width: 6), - ElevatedButton(onPressed: () { - final updatedText = _textEditingController.text + '['; - setState(() { - _textEditingController.text = updatedText; - }); - }, child: Text("[")), + ElevatedButton( + onPressed: () { + final updatedText = + _textEditingController.text + '['; + setState(() { + _textEditingController.text = updatedText; + }); + }, + child: Text("[")), const SizedBox(width: 6), - ElevatedButton(onPressed: () { - final updatedText = _textEditingController.text + ']'; - setState(() { - _textEditingController.text = updatedText; - }); - }, child: Text("]")), + ElevatedButton( + onPressed: () { + final updatedText = + _textEditingController.text + ']'; + setState(() { + _textEditingController.text = updatedText; + }); + }, + child: Text("]")), ], ) ])) diff --git a/lib/presentation/tools/widget/result_3by3_matrix.dart b/lib/presentation/tools/widget/result_3by3_matrix.dart index cfe9acf..6a096c3 100644 --- a/lib/presentation/tools/widget/result_3by3_matrix.dart +++ b/lib/presentation/tools/widget/result_3by3_matrix.dart @@ -7,9 +7,9 @@ import 'package:swiftcomp/util/NumberPrecisionHelper.dart'; class Result3By3Matrix extends StatelessWidget { final String title; - final Matrix matrix; + final List> matrixList; - const Result3By3Matrix({Key? key, required this.matrix, required this.title}) : super(key: key); + const Result3By3Matrix({Key? key, required this.matrixList, required this.title}) : super(key: key); @override Widget build(BuildContext context) { @@ -32,7 +32,7 @@ class Result3By3Matrix extends StatelessWidget { crossAxisCount: 6, itemCount: 9, itemBuilder: (BuildContext context, int index) { - double value = matrix[index ~/ 3][index % 3]; + double value = matrixList[index ~/ 3][index % 3]; return Consumer(builder: (context, precs, child) { return Center( child: Container( diff --git a/lib/presentation/tools/widget/transversely_thermal_constants_row.dart b/lib/presentation/tools/widget/transversely_thermal_constants_row.dart index 47832cd..a1ea961 100644 --- a/lib/presentation/tools/widget/transversely_thermal_constants_row.dart +++ b/lib/presentation/tools/widget/transversely_thermal_constants_row.dart @@ -71,7 +71,7 @@ class _TransverselyThermalConstantsRowState extends State