Line data Source code
1 : // Copyright 2024, UChicago Argonne, LLC
2 : // All Rights Reserved
3 : // Software Name: NEML2 -- the New Engineering material Model Library, version 2
4 : // By: Argonne National Laboratory
5 : // OPEN SOURCE LICENSE (MIT)
6 : //
7 : // Permission is hereby granted, free of charge, to any person obtaining a copy
8 : // of this software and associated documentation files (the "Software"), to deal
9 : // in the Software without restriction, including without limitation the rights
10 : // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 : // copies of the Software, and to permit persons to whom the Software is
12 : // furnished to do so, subject to the following conditions:
13 : //
14 : // The above copyright notice and this permission notice shall be included in
15 : // all copies or substantial portions of the Software.
16 : //
17 : // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 : // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 : // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 : // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 : // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 : // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 : // THE SOFTWARE.
24 :
25 : #include "neml2/drivers/solid_mechanics/SolidMechanicsDriver.h"
26 : #include "neml2/misc/assertions.h"
27 :
28 : namespace neml2
29 : {
30 : OptionSet
31 4 : SolidMechanicsDriver::expected_options()
32 : {
33 4 : OptionSet options = TransientDriver::expected_options();
34 4 : options.doc() = "Driver for solid mechanics material model with optional thermal coupling.";
35 :
36 16 : EnumSelection control_selection({"STRAIN", "STRESS", "MIXED"}, "STRAIN");
37 4 : options.set<EnumSelection>("control") = control_selection;
38 8 : options.set("control").doc() =
39 12 : "External control of the material update. Options are " + control_selection.candidates_str();
40 :
41 12 : options.set<VariableName>("temperature") = VariableName(FORCES, "T");
42 8 : options.set("temperature").doc() = "Name of temperature";
43 8 : options.set<TensorName<Scalar>>("prescribed_temperature");
44 8 : options.set("prescribed_temperature").doc() =
45 4 : "Actual prescibed temperature values, when providing temperatures to the model";
46 :
47 12 : options.set<VariableName>("mixed_driving_force") = VariableName(FORCES, "fixed_values");
48 8 : options.set("mixed_driving_force").doc() = "Name of mixed driving force when using mixed control";
49 8 : options.set<TensorName<SR2>>("prescribed_mixed_driving_force");
50 8 : options.set("prescribed_mixed_driving_force").doc() =
51 : "The fixed, controlled values provided as user input for the mixed control case. Where the "
52 : "control signal is 0 these are strain/deformation values, where it is 1 these are stress "
53 4 : "values";
54 :
55 12 : options.set<VariableName>("mixed_control_signal") = VariableName(FORCES, "control");
56 12 : options.set("mixed_control_signal").doc() =
57 4 : "The name of the control signal for mixed control on the input axis";
58 8 : options.set<TensorName<SR2>>("prescribed_mixed_control_signal");
59 8 : options.set("prescribed_mixed_control_signal").doc() =
60 : "The actual values of the control signal for mixed control. 0 implies strain/deformation "
61 4 : "control, 1 implies stress control";
62 :
63 8 : return options;
64 4 : }
65 :
66 7 : SolidMechanicsDriver::SolidMechanicsDriver(const OptionSet & options)
67 : : TransientDriver(options),
68 7 : _control(options.get<EnumSelection>("control")),
69 28 : _temperature_prescribed(options.get("prescribed_temperature").user_specified())
70 : {
71 7 : }
72 :
73 : void
74 7 : SolidMechanicsDriver::setup()
75 : {
76 7 : TransientDriver::setup();
77 :
78 14 : if (_control == "STRAIN")
79 5 : init_strain_control(input_options());
80 4 : else if (_control == "STRESS")
81 1 : init_stress_control(input_options());
82 2 : else if (_control == "MIXED")
83 1 : init_mixed_control(input_options());
84 : else
85 : // LCOV_EXCL_START
86 : throw NEMLException("Unsupported control type.");
87 : // LCOV_EXCL_STOP
88 :
89 7 : if (_temperature_prescribed)
90 1 : init_temperature_control(input_options());
91 7 : }
92 :
93 : void
94 1 : SolidMechanicsDriver::init_mixed_control(const OptionSet & options)
95 : {
96 1 : _driving_force_name = options.get<VariableName>("mixed_driving_force");
97 2 : _driving_force = resolve_tensor<SR2>("prescribed_mixed_driving_force");
98 1 : _driving_force = _driving_force.to(_device);
99 :
100 1 : _mixed_control_name = options.get<VariableName>("mixed_control_signal");
101 2 : _mixed_control = resolve_tensor<SR2>("prescribed_mixed_control_signal");
102 1 : _mixed_control = _mixed_control.to(_device);
103 1 : }
104 :
105 : void
106 1 : SolidMechanicsDriver::init_temperature_control(const OptionSet & options)
107 : {
108 1 : _temperature_name = options.get<VariableName>("temperature");
109 2 : _temperature = resolve_tensor<Scalar>("prescribed_temperature");
110 1 : _temperature = _temperature.to(_device);
111 1 : }
112 :
113 : void
114 7 : SolidMechanicsDriver::diagnose() const
115 : {
116 7 : TransientDriver::diagnose();
117 :
118 7 : diagnostic_assert(_driving_force.batch_dim() >= 1,
119 : "Input driving force (strain, stress, or mixed conditions) should have at "
120 : "least one batch dimension for time steps but instead has batch dimension ",
121 7 : _driving_force.batch_dim());
122 :
123 7 : diagnostic_assert(_time.batch_size(0) == _driving_force.batch_size(0),
124 : "Input driving force (strain, stress, or mixed conditions) and time should "
125 : "have the same number of time steps. The input time has ",
126 14 : _time.batch_size(0),
127 : " time steps, while the input driving force has ",
128 14 : _driving_force.batch_size(0),
129 : " time steps");
130 :
131 7 : if (_temperature_prescribed)
132 : {
133 1 : diagnostic_assert(_temperature.batch_dim() >= 1,
134 : "Input temperature should have at least one batch dimension for time steps "
135 : "but instead has batch dimension ",
136 1 : _temperature.batch_dim());
137 :
138 1 : diagnostic_assert(
139 1 : _time.batch_size(0) == _temperature.batch_size(0),
140 : "Input temperature and time should have the same number of time steps. The input time has ",
141 2 : _time.batch_size(0),
142 : " time steps, while the input temperature has ",
143 2 : _temperature.batch_size(0),
144 : " time steps");
145 : }
146 :
147 14 : if (_control == "MIXED")
148 : {
149 1 : diagnostic_assert(_mixed_control.batch_dim() >= 1,
150 : "Input control signal should have at least one batch dimension but instead "
151 : "has batch dimension ",
152 1 : _mixed_control.batch_dim());
153 1 : diagnostic_assert(
154 1 : _mixed_control.batch_size(0) == _time.batch_size(0),
155 : "Input control signal should have the same number of steps steps as time, but instead has ",
156 2 : _mixed_control.batch_size(0),
157 : " time steps");
158 : }
159 7 : }
160 :
161 : void
162 70 : SolidMechanicsDriver::update_forces()
163 : {
164 70 : TransientDriver::update_forces();
165 :
166 140 : _in[_driving_force_name] = _driving_force.batch_index({_step_count});
167 :
168 70 : if (_temperature_prescribed)
169 20 : _in[_temperature_name] = _temperature.batch_index({_step_count});
170 :
171 140 : if (_control == "MIXED")
172 20 : _in[_mixed_control_name] = _mixed_control.batch_index({_step_count});
173 160 : }
174 : }
|