170170 Origin=nothing, StrikeAngle=0, DipAngle=0,
171171 phase = ConstantPhase(1),
172172 T=nothing,
173+ segments=nothing,
173174 cell=false )
174175
175176Adds a box with phase & temperature structure to a 3D model setup. This simplifies creating model geometries in geodynamic models
@@ -188,6 +189,7 @@ Parameters
188189- `DipAngle` - dip angle of slab
189190- `phase` - specifies the phase of the box. See `ConstantPhase()`,`LithosphericPhases()`
190191- `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
191193- `cell` - if true, `Phase` and `Temp` are defined on centers
192194
193195Examples
@@ -223,15 +225,32 @@ julia> write_paraview(Grid,"LaMEM_ModelSetup") # Save model to paraview
2232251-element Vector{String}:
224226 "LaMEM_ModelSetup.vts"
225227```
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"
226245"""
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
231249 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+
235254
236255 # Retrieve 3D data arrays for the grid
237256 X, Y, Z = coordinate_grids (Grid, cell = cell)
@@ -279,17 +298,19 @@ function add_box!(
279298 if isa (T, LithosphericTemp)
280299 Phase[ind_flat] = compute_phase (Phase[ind_flat], Temp[ind_flat], Xrot[ind], Yrot[ind], Zrot[ind], phase)
281300 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
283306 end
284-
285307 # Set the phase. Different routines are available for that - see below.
286308 Phase[ind_flat] = compute_phase (Phase[ind_flat], Temp[ind_flat], Xrot[ind], Yrot[ind], Zrot[ind], phase)
287309 end
288310
289311 return nothing
290312end
291313
292-
293314"""
294315 add_layer!(Phase, Temp, Grid::AbstractGeneralGrid; xlim::Tuple = (1,100), [ylim::Tuple = (0,20)], zlim::Tuple = (0,-100),
295316 phase = ConstantPhase(1),
@@ -1126,31 +1147,32 @@ Note: the thermal age at the mid oceanic ridge is set to 1 year to avoid divisio
11261147 Adiabat = 0 # Adiabatic gradient in K/km
11271148 MORside = " left" # side of box where the MOR is located
11281149 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]
11311152end
11321153
11331154function 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;
11411171
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
11501172 else
11511173 error (" unknown side" )
11521174 end
1153-
1175+
11541176 for i in eachindex (Temp)
11551177 ThermalAge = abs (Distance[i] * 1.0e3 * 1.0e2 ) / SpreadingVel + AgeRidge * 1.0e6 # Thermal age in years
11561178 if ThermalAge > maxAge * 1.0e6
@@ -1163,10 +1185,112 @@ function compute_thermal_structure(Temp, X, Y, Z, Phase, s::SpreadingRateTemp)
11631185 end
11641186
11651187 Temp[i] = (Tsurface .- Tmantle) * erfc ((abs .(Z[i]) * 1.0e3 ) ./ (2 * sqrt (kappa * ThermalAge))) + MantleAdiabaticT[i]
1188+
11661189 end
1190+
11671191 return Temp
11681192end
11691193
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+
11701294"""
11711295 LithosphericTemp(Tsurface=0.0, Tpot=1350.0, dTadi=0.5,
11721296 ubound="const", lbound="const, utbf = 50.0e-3, ltbf = 10.0e-3,
0 commit comments