170
170
Origin=nothing, StrikeAngle=0, DipAngle=0,
171
171
phase = ConstantPhase(1),
172
172
T=nothing,
173
+ segments=nothing,
173
174
cell=false )
174
175
175
176
Adds a box with phase & temperature structure to a 3D model setup. This simplifies creating model geometries in geodynamic models
@@ -188,6 +189,7 @@ Parameters
188
189
- `DipAngle` - dip angle of slab
189
190
- `phase` - specifies the phase of the box. See `ConstantPhase()`,`LithosphericPhases()`
190
191
- `T` - specifies the temperature of the box. See `ConstantTemp()`,`LinearTemp()`,`HalfspaceCoolingTemp()`,`SpreadingRateTemp()`,`LithosphericTemp()`
192
+ - `segments` - optional parameter to define multiple ridge segments within the box
191
193
- `cell` - if true, `Phase` and `Temp` are defined on centers
192
194
193
195
Examples
@@ -223,15 +225,32 @@ julia> write_paraview(Grid,"LaMEM_ModelSetup") # Save model to paraview
223
225
1-element Vector{String}:
224
226
"LaMEM_ModelSetup.vts"
225
227
```
228
+
229
+ Example 3) Box with ridge thermal structure
230
+ ```julia-repl
231
+ julia> Grid = CartData(xyz_grid(-1000:10:1000, -1000:10:1000, -660:5:0))
232
+ julia> Phases = fill(2, size(Grid));
233
+ julia> Temp = fill(1350.0, size(Grid));
234
+ julia> segments = [((-500.0, -1000.0), (-500.0, 0.0)),
235
+ ((-250.0, 0.0), (-250.0, 200.0)),
236
+ ((-750.0, 200.0), (-750.0, 1000.0))];
237
+ julia> lith = LithosphericPhases(Layers=[15 55], Phases=[1 2], Tlab=1250);
238
+ julia> add_box!(Phases, Temp, Grid; xlim=(-1000.0, 0.0), ylim=(-500.0, 500.0),
239
+ zlim=(-80.0, 0.0), phase=lith,
240
+ T=SpreadingRateTemp(SpreadingVel=3), segments=segments)
241
+ julia> Grid = addfield(Grid, (; Phases, Temp)); # Add to Cartesian model
242
+ julia> write_paraview(Grid, "Ridge_Thermal_Structure") # Save model to Paraview
243
+ 1-element Vector{String}:
244
+ "Ridge_Thermal_Structure.vts"
226
245
"""
227
- function add_box! (
228
- Phase, Temp, Grid:: AbstractGeneralGrid ; # required input
229
- xlim:: Tuple = (20 , 100 ), ylim = nothing , zlim:: Tuple = (10 , 80 ), # limits of the box
230
- Origin = nothing , StrikeAngle = 0 , DipAngle = 0 , # origin & dip/strike
246
+ function add_box! (Phase, Temp, Grid:: AbstractGeneralGrid ; # required input
247
+ xlim:: Tuple = (20 ,100 ), ylim= nothing , zlim:: Tuple = (10 ,80 ), # limits of the box
248
+ Origin= nothing , StrikeAngle= 0 , DipAngle= 0 , # origin & dip/strike
231
249
phase = ConstantPhase (1 ), # Sets the phase number(s) in the box
232
- T = nothing , # Sets the thermal structure (various functions are available)
233
- cell = false
234
- ) # if true, Phase and Temp are defined on cell centers
250
+ T= nothing , # Sets the thermal structure (various functions are available)
251
+ segments= nothing , # Allows defining multiple ridge segments
252
+ cell= false ) # if true, Phase and Temp are defined on cell centers
253
+
235
254
236
255
# Retrieve 3D data arrays for the grid
237
256
X, Y, Z = coordinate_grids (Grid, cell = cell)
@@ -279,17 +298,19 @@ function add_box!(
279
298
if isa (T, LithosphericTemp)
280
299
Phase[ind_flat] = compute_phase (Phase[ind_flat], Temp[ind_flat], Xrot[ind], Yrot[ind], Zrot[ind], phase)
281
300
end
282
- Temp[ind_flat] = compute_thermal_structure (Temp[ind_flat], Xrot[ind], Yrot[ind], Zrot[ind], Phase[ind_flat], T)
301
+ if segments != = nothing
302
+ Temp[ind_flat] = compute_thermal_structure (Temp[ind_flat], X[ind], Y[ind], Z[ind], Phase[ind_flat], T, segments)
303
+ else
304
+ Temp[ind_flat] = compute_thermal_structure (Temp[ind_flat], Xrot[ind], Yrot[ind], Zrot[ind], Phase[ind_flat], T)
305
+ end
283
306
end
284
-
285
307
# Set the phase. Different routines are available for that - see below.
286
308
Phase[ind_flat] = compute_phase (Phase[ind_flat], Temp[ind_flat], Xrot[ind], Yrot[ind], Zrot[ind], phase)
287
309
end
288
310
289
311
return nothing
290
312
end
291
313
292
-
293
314
"""
294
315
add_layer!(Phase, Temp, Grid::AbstractGeneralGrid; xlim::Tuple = (1,100), [ylim::Tuple = (0,20)], zlim::Tuple = (0,-100),
295
316
phase = ConstantPhase(1),
@@ -1126,31 +1147,32 @@ Note: the thermal age at the mid oceanic ridge is set to 1 year to avoid divisio
1126
1147
Adiabat = 0 # Adiabatic gradient in K/km
1127
1148
MORside = " left" # side of box where the MOR is located
1128
1149
SpreadingVel = 3 # spreading velocity [cm/yr]
1129
- AgeRidge = 0 # Age of the ridge [Myrs]
1130
- maxAge = 60 # maximum thermal age of plate [Myrs]
1150
+ AgeRidge = 0 # Age of the ridge [Myrs
1151
+ maxAge = 60 # maximum thermal age of plate [Myrs]
1131
1152
end
1132
1153
1133
1154
function compute_thermal_structure (Temp, X, Y, Z, Phase, s:: SpreadingRateTemp )
1134
- @unpack Tsurface, Tmantle, Adiabat, MORside, SpreadingVel, AgeRidge, maxAge = s
1135
-
1136
- kappa = 1.0e-6
1137
- SecYear = 3600 * 24 * 365
1138
- dz = Z[end ] - Z[1 ]
1139
-
1140
- MantleAdiabaticT = Tmantle .+ Adiabat * abs .(Z) # Adiabatic temperature of mantle
1155
+ @unpack Tsurface, Tmantle, Adiabat, MORside, SpreadingVel, AgeRidge, maxAge = s
1156
+
1157
+ kappa = 1e-6 ;
1158
+ SecYear = 3600 * 24 * 365
1159
+ dz = Z[end ]- Z[1 ];
1160
+
1161
+ MantleAdiabaticT = Tmantle .+ Adiabat* abs .(Z); # Adiabatic temperature of mantle
1162
+
1163
+ if MORside== " left"
1164
+ Distance = X .- X[1 ,1 ,1 ];
1165
+ elseif MORside== " right"
1166
+ Distance = X[end ,1 ,1 ] .- X;
1167
+ elseif MORside== " front"
1168
+ Distance = Y .- Y[1 ,1 ,1 ];
1169
+ elseif MORside== " back"
1170
+ Distance = Y[1 ,end ,1 ] .- Y;
1141
1171
1142
- if MORside == " left"
1143
- Distance = X .- X[1 , 1 , 1 ]
1144
- elseif MORside == " right"
1145
- Distance = X[end , 1 , 1 ] .- X
1146
- elseif MORside == " front"
1147
- Distance = Y .- Y[1 , 1 , 1 ]
1148
- elseif MORside == " back"
1149
- Distance = Y[1 , end , 1 ] .- Y
1150
1172
else
1151
1173
error (" unknown side" )
1152
1174
end
1153
-
1175
+
1154
1176
for i in eachindex (Temp)
1155
1177
ThermalAge = abs (Distance[i] * 1.0e3 * 1.0e2 ) / SpreadingVel + AgeRidge * 1.0e6 # Thermal age in years
1156
1178
if ThermalAge > maxAge * 1.0e6
@@ -1163,10 +1185,112 @@ function compute_thermal_structure(Temp, X, Y, Z, Phase, s::SpreadingRateTemp)
1163
1185
end
1164
1186
1165
1187
Temp[i] = (Tsurface .- Tmantle) * erfc ((abs .(Z[i]) * 1.0e3 ) ./ (2 * sqrt (kappa * ThermalAge))) + MantleAdiabaticT[i]
1188
+
1166
1189
end
1190
+
1167
1191
return Temp
1168
1192
end
1169
1193
1194
+ """
1195
+ SpreadingRateTemp(Temp, X, Y, Z, Phase, s::SpreadingRateTemp, segments)
1196
+
1197
+ Calculates the temperature distribution across the plate considering multiple ridge segments.
1198
+
1199
+ This function computes the thermal structure based on the perpendicular distance from each point to its corresponding ridge segment, and applies a thermal model using a spreading velocity and thermal age.
1200
+
1201
+ Parameters
1202
+ ==========
1203
+ - Temp : Temperature field to be updated (array)
1204
+ - X, Y, Z : Coordinates of the points (arrays)
1205
+ - Phase : Phase of the material (unused in this version)
1206
+ - s : SpreadingRateTemp object containing the thermal and spreading parameters
1207
+ - segments : List of ridge segments, where each segment is defined by two tuples representing the start and end coordinates (x1, y1) and (x2, y2) for each segment.
1208
+
1209
+ Note
1210
+ ====
1211
+ The temperature at each point is calculated using the thermal age, which is determined by the distance from the point to the nearest ridge segment and the spreading velocity.
1212
+
1213
+ The function works in the context of one or more segments. The key difference from the previous function is that the ridge can now be placed at any position within the box, not just at the boundary.
1214
+
1215
+ The thermal age is capped at `maxAge` years, and the temperature is adjusted based on the distance to the ridge and the corresponding thermal gradient.
1216
+
1217
+ """
1218
+
1219
+ function compute_thermal_structure (Temp, X, Y, Z, Phase, s:: SpreadingRateTemp , segments:: Vector{Tuple{Tuple{Float64, Float64}, Tuple{Float64, Float64}}} )
1220
+ @unpack Tsurface, Tmantle, Adiabat, SpreadingVel, AgeRidge, maxAge = s
1221
+ kappa = 1e-6 ;
1222
+ SecYear = 3600 * 24 * 365
1223
+ dz = Z[end ]- Z[1 ];
1224
+
1225
+ MantleAdiabaticT = Tmantle .+ Adiabat * abs .(Z)
1226
+
1227
+ # Create delimiters
1228
+ delimiters = [(segments[i][2 ], segments[i + 1 ][1 ]) for i in 1 : length (segments) - 1 ]
1229
+
1230
+ for I in eachindex (X)
1231
+ px, py, pz = X[I], Y[I], Z[I]
1232
+
1233
+ # Determine region of point
1234
+ region = determine_region (px, py, delimiters, segments)
1235
+
1236
+ # Select the corresponding segment
1237
+ x1, y1 = segments[region][1 ]
1238
+ x2, y2 = segments[region][2 ]
1239
+
1240
+ # Calculate distance to segment
1241
+ Distance = perpendicular_distance_to_segment (px, py, x1, y1, x2, y2)
1242
+
1243
+ # Calculate thermal age
1244
+ ThermalAge = abs (Distance * 1e5 ) / SpreadingVel + AgeRidge * 1e6 # Thermal age in years
1245
+ if ThermalAge > maxAge * 1e6
1246
+ ThermalAge = maxAge * 1e6
1247
+ end
1248
+
1249
+ ThermalAge = ThermalAge * SecYear # Convert to seconds
1250
+ if ThermalAge == 0
1251
+ ThermalAge = 1e-6 # Avoid zero
1252
+ end
1253
+
1254
+ # Calculate temperature
1255
+ Temp[I] = (Tsurface - Tmantle) * erfc (abs (pz) * 1e3 / (2 * sqrt (kappa * ThermalAge))) + MantleAdiabaticT[I]
1256
+ end
1257
+
1258
+ return Temp
1259
+
1260
+ end
1261
+
1262
+ # Supporting functions for multi-segment ridge functionality
1263
+
1264
+ # Function to calculate the perpendicular distance from a point to a segment
1265
+ function perpendicular_distance_to_segment (x, y, x1, y1, x2, y2)
1266
+ num = abs ((y2 - y1) * x - (x2 - x1) * y + x2 * y1 - y2 * x1)
1267
+ den = sqrt ((y2 - y1)^ 2 + (x2 - x1)^ 2 )
1268
+ return num / den
1269
+ end
1270
+
1271
+ # Function to determine the side of a point with respect to a line (adjusted for segment direction)
1272
+ function side_of_line (x, y, x1, y1, x2, y2, direction)
1273
+ side = (x2 - x1) * (y - y1) - (y2 - y1) * (x - x1)
1274
+ direction == :left ? side > 0 : side < 0
1275
+ end
1276
+
1277
+ # Function to determine in which region a point lies (based on delimiters)
1278
+ function determine_region (px, py, delimiters, segments)
1279
+ for i in 1 : length (delimiters)
1280
+ x1, y1 = delimiters[i][1 ]
1281
+ x2, y2 = delimiters[i][2 ]
1282
+
1283
+ # Determine the direction of the segments
1284
+ direction = x2 < x1 ? :left : :right
1285
+
1286
+ # Check the side of the line considering the direction
1287
+ if side_of_line (px, py, x1, y1, x2, y2, direction)
1288
+ return i # Region corresponding to segment i
1289
+ end
1290
+ end
1291
+ return length (segments) # Last region
1292
+ end
1293
+
1170
1294
"""
1171
1295
LithosphericTemp(Tsurface=0.0, Tpot=1350.0, dTadi=0.5,
1172
1296
ubound="const", lbound="const, utbf = 50.0e-3, ltbf = 10.0e-3,
0 commit comments