-
Notifications
You must be signed in to change notification settings - Fork 43
Add temperature models that reads from ascii data file #830
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
jdannberg
wants to merge
2
commits into
GeodynamicWorldBuilder:main
Choose a base branch
from
jdannberg:ascii_data
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 1 commit
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,229 @@ | ||
| import numpy as np | ||
| import matplotlib.pyplot as plt | ||
| from scipy.special import erf | ||
|
|
||
| # --- 1. Parameters --- | ||
| L = 5.e5 # Depth of the domain (m) | ||
| N = 501 # Number of spatial points | ||
| dx = L / (N - 1) # Grid spacing | ||
| x = np.linspace(0, L, N) | ||
|
|
||
| # Physical properties (example) | ||
| alpha = 3e-5 # 1/K | ||
| cp = 1200 # Specific heat capacity (J/kg-K), assumed constant for simplicity | ||
| reference_rho = 3300 | ||
| k0 = 4 # W/m/K | ||
| reference_T = 293 | ||
| gravity = 10 | ||
| T_surface = 293. | ||
|
|
||
| # 'T-dependent' or 'constant' | ||
| formulation = 'T-dependent' | ||
|
|
||
| # Time stepping | ||
| year_in_seconds = 3600*24*365.25 | ||
| dt = 1e4 * year_in_seconds # Time step (yr) -- must be small enough for stability | ||
| t_max = 2.5e8 * year_in_seconds # Total time (yr) | ||
| n_steps = int(t_max / dt) | ||
|
|
||
| save_interval = int(n_steps / 250) # how often we store temperature, pressure, density | ||
|
|
||
| # Let's define a variable density and variable conductivity as functions of x | ||
| def density(T): | ||
| if formulation == 'T-dependent': | ||
| return reference_rho * (1. - alpha * (T - reference_T)) | ||
| elif formulation == 'constant': | ||
| return reference_rho * np.ones(T.shape) | ||
|
|
||
| def conductivity(T, p): | ||
| # Hofmeister | ||
| if formulation == 'T-dependent': | ||
| k = k0 * (298.0/T)**0.33 \ | ||
| * np.exp(-(4.0 * 1.2 + 1./3.) * alpha * (T - 298.0)) \ | ||
| * (1. + 4. * p / 1.35e11) \ | ||
| + 0.01753 - 0.00010365 * T + 2.2451e-7 * T**2 - 3.407e-11 * T**3 | ||
| elif formulation == 'constant': | ||
| k = k0 * np.ones(T.shape) | ||
| return k | ||
|
|
||
| def integrate_pressure(rho_arr): | ||
| # Here, for if rho depends on p, we would need to update rho as well | ||
| p_arr = np.zeros(N) | ||
| for i in range(1, N): | ||
| p_arr[i] = p_arr[i-1] + rho_arr[i] * gravity * dx | ||
| return p_arr | ||
|
|
||
|
|
||
| # --- 2. Initial Condition --- | ||
| T = np.zeros(N) | ||
|
|
||
| # Start with mantle potential temperature | ||
| T_mantle = 1623.0 | ||
| T[:] = T_mantle | ||
| T[0] = T_surface | ||
|
|
||
| # --- 3. Boundary Conditions (Dirichlet) --- | ||
| # Use the initial conditions | ||
| T_top = T[0] | ||
| T_bottom = T[-1] | ||
|
|
||
| # Helper to get "interface conductivity" k_{i+1/2} | ||
| def k_interface(k_top, k_bot): | ||
| # For simplicity, use average. Could also use harmonic mean, etc. | ||
| return 0.5 * (k_top + k_bot) | ||
|
|
||
| # --- 4. Time-stepping loop (Explicit) --- | ||
|
|
||
| for step in range(n_steps): | ||
| T_new = T.copy() | ||
|
|
||
| # Evaluate properties at the current time | ||
| rho_arr = density(T) | ||
|
|
||
| # compute pressure | ||
| p_arr = integrate_pressure(rho_arr) | ||
| k_arr = conductivity(T, p_arr) | ||
|
|
||
| if step == 0: | ||
| # keep track of solutions for plotting | ||
| T_history = [T.copy()] | ||
| p_history = [p_arr.copy()] | ||
| rho_history = [rho_arr.copy()] | ||
| k_history = [k_arr.copy()] | ||
|
|
||
| # Enforce Dirichlet boundaries | ||
| T_new[0] = T_top | ||
| T_new[-1] = T_bottom | ||
|
|
||
| # Update interior points | ||
| for i in range(1, N-1): | ||
| rho_i = rho_arr[i] | ||
|
|
||
| # Compute effective conductivities at the interfaces | ||
| k_imh = k_interface(k_arr[i-1], k_arr[i]) # k_{i-1/2} | ||
| k_iph = k_interface(k_arr[i], k_arr[i+1]) # k_{i+1/2} | ||
|
|
||
| # Finite difference for second derivative with variable k | ||
| d2T_dx2 = (k_iph*(T[i+1] - T[i]) - k_imh*(T[i] - T[i-1])) / (dx*dx) | ||
|
|
||
| # Explicit update | ||
| T_new[i] = T[i] + dt/(rho_i * cp) * d2T_dx2 | ||
|
|
||
| # Advance in time | ||
| T = T_new | ||
|
|
||
| # Store in history every so often | ||
| # Do not store step 0 again | ||
| # Note that technically, only T is from after solving, the other properties have not been updated | ||
| if (step+1) % save_interval == 0: | ||
| T_history.append(T.copy()) | ||
| p_history.append(p_arr.copy()) | ||
| rho_history.append(rho_arr.copy()) | ||
| k_history.append(k_arr.copy()) | ||
|
|
||
| # build time array for each saved snapshot | ||
| nt = len(T_history) # number of snapshots actually stored | ||
| t = np.arange(nt) * (save_interval * dt) | ||
|
|
||
| # --- 5. Plot final solution --- | ||
|
|
||
| # T_2D is (nt, N), so we can do .T to get (N, nt) | ||
| Temperature_2D = np.array(T_history).T | ||
| pressure_2D = np.array(p_history).T | ||
| density_2D = np.array(rho_history).T | ||
| conductivity_2D = np.array(k_history).T | ||
|
|
||
| # Make coordinate grids | ||
| # - "t" along the 'horizontal' direction (axis=1 or 0 depends on your preference) | ||
| # - "x" along the 'vertical' direction | ||
|
|
||
| Xgrid, timegrid = np.meshgrid(x, t/(year_in_seconds * 1e6), indexing='ij') | ||
| # Now Xgrid and timegrid both have shape (N, nt) | ||
|
|
||
|
|
||
| plt.figure(figsize=(10,4)) | ||
| # Make a "heatmap" of temperature. | ||
| # Xgrid, timegrid, and Temperature_2D must be the same shape (N, nt). | ||
|
|
||
| plt.pcolormesh(timegrid, Xgrid/1000, Temperature_2D, shading='gouraud', edgecolors='face', linewidth=0) | ||
| plt.xlabel("Time [Myr]") | ||
| plt.ylabel("Depth [km]") | ||
| plt.gca().invert_yaxis() | ||
| cbar = plt.colorbar() | ||
| cbar.set_label("Temperature [K]") | ||
|
|
||
| plt.title("Temperature evolution") | ||
| plt.savefig('temperature.pdf', format='pdf') | ||
| plt.close() | ||
|
|
||
|
|
||
| plt.figure(figsize=(10,4)) | ||
| plt.pcolormesh(timegrid, Xgrid/1000, pressure_2D - reference_rho * gravity * Xgrid, shading='gouraud', edgecolors='face', linewidth=0) | ||
| plt.xlabel("Time [Myr]") | ||
| plt.ylabel("Depth [km]") | ||
| plt.gca().invert_yaxis() | ||
| cbar = plt.colorbar() | ||
| cbar.set_label("Pressure [Pa]") | ||
|
|
||
| plt.title("Pressure difference from $rho_0 g z$") | ||
| plt.savefig('pressure.pdf', format='pdf') | ||
| plt.close() | ||
|
|
||
|
|
||
| plt.figure(figsize=(10,4)) | ||
| plt.pcolormesh(timegrid, Xgrid/1000, density_2D, shading='gouraud', edgecolors='face', linewidth=0) | ||
| plt.xlabel("Time [Myr]") | ||
| plt.ylabel("Depth [km]") | ||
| plt.gca().invert_yaxis() | ||
| cbar = plt.colorbar() | ||
| cbar.set_label("Density [kg/m$^3$]") | ||
|
|
||
| plt.title("Density evolution") | ||
| plt.savefig('density.pdf', format='pdf') | ||
| plt.close() | ||
|
|
||
|
|
||
| plt.figure(figsize=(10,4)) | ||
| plt.pcolormesh(timegrid, Xgrid/1000, conductivity_2D, shading='gouraud', edgecolors='face', linewidth=0) | ||
| plt.xlabel("Time [Myr]") | ||
| plt.ylabel("Depth [km]") | ||
| plt.gca().invert_yaxis() | ||
| cbar = plt.colorbar() | ||
| cbar.set_label("Conductivity [W/m/K]") | ||
|
|
||
| plt.title("Conductivity evolution") | ||
| plt.savefig('conductivity.pdf', format='pdf') | ||
| plt.close() | ||
|
|
||
| # Compare to erfc. | ||
| plt.figure(figsize=(10,4)) | ||
| HSC_grid = T_surface + (T_mantle - T_surface) * erf(Xgrid/(2*np.sqrt(conductivity_2D*timegrid*year_in_seconds*1e6/(density_2D*cp)))) | ||
| plt.pcolormesh(timegrid, Xgrid/1000, Temperature_2D - HSC_grid, shading='gouraud', edgecolors='face', linewidth=0) | ||
| plt.xlabel("Time [Myr]") | ||
| plt.ylabel("Depth [km]") | ||
| plt.gca().invert_yaxis() | ||
| cbar = plt.colorbar() | ||
| cbar.set_label("Temperature [K]") | ||
|
|
||
| plt.title("Temperature - error function") | ||
| plt.savefig('temperature_erfc.pdf', format='pdf') | ||
| plt.close() | ||
|
|
||
|
|
||
| # Write output into file | ||
| filename = "temperature_data.txt" | ||
|
|
||
| with open(filename, "w") as f: | ||
| f.write("# Columns: time [yrs] depth [m] temperature\n") | ||
| f.write("# POINTS: " + str(len(t)) + " " + str(len(x)) + "\n") | ||
| for ix in range(len(x)): # loop over time snapshots | ||
| for it in range(len(t)): # loop over the 50 x-values | ||
| # Each line: time, depth, T | ||
| # Right align each with chosen width and precision | ||
| time_str = f"{t[it]/year_in_seconds:9.3e}" | ||
| x_str = f"{x[ix]:12.3e}" | ||
| temp_str = f"{Temperature_2D[ix, it]:12.3f}" | ||
|
|
||
| f.write(time_str + x_str + temp_str + "\n") | ||
|
|
||
| print(f"Data written to '{filename}'") |
14 changes: 14 additions & 0 deletions
14
cookbooks/2d_cartesian_plate_ascii/2d_cartesian_plate_ascii.grid
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| # output variables | ||
| grid_type = cartesian | ||
| dim = 2 | ||
| compositions = 5 | ||
|
|
||
| # domain of the grid | ||
| x_min = 0e3 | ||
| x_max = 2000e3 | ||
| z_min = 0 | ||
| z_max = 400e3 | ||
|
|
||
| # grid properties | ||
| n_cell_x = 2000 | ||
| n_cell_z = 400 |
42 changes: 42 additions & 0 deletions
42
cookbooks/2d_cartesian_plate_ascii/2d_cartesian_plate_ascii.wb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| { | ||
| "version":"1.1", | ||
| "surface temperature":293, | ||
| "potential mantle temperature":1623, | ||
| "thermal expansion coefficient":3e-5, | ||
| "specific heat":1200, | ||
| "gravity model":{"model":"uniform", "magnitude":10}, | ||
| "thermal diffusivity":1.06060606e-6, | ||
| "cross section":[[0,0],[100,0]], | ||
| "features": | ||
| [ | ||
| {"model":"mantle layer", "name":"upper mantle", "min depth":95e3, "max depth":660e3, "coordinates":[[-1e3,-1e3],[2001e3,-1e3],[2001e3,1e3],[-1e3,1e3]], | ||
| "temperature models":[{"model":"adiabatic", "min depth":95e3, "max depth":660e3}], | ||
| "composition models":[{"model":"uniform", "compositions":[4]}]}, | ||
|
|
||
| {"model":"oceanic plate", "name":"oceanic plate", "coordinates":[[-1e3,-1e3],[1150e3,-1e3],[1150e3,1e3],[-1e3,1e3]], | ||
| "temperature models":[{"model":"ascii data", "max depth":300e3, "spreading velocity":0.01, "ridge coordinates":[[[100e3,-1e3],[100e3,1e3]]]}], | ||
| "composition models":[{"model":"uniform", "compositions":[0], "max depth":10e3}, | ||
| {"model":"uniform", "compositions":[1], "min depth":10e3, "max depth":95e3}]}, | ||
|
|
||
| {"model":"continental plate", "name":"continental plate", "coordinates":[[1150e3,-1e3],[2001e3,-1e3],[2001e3,1e3],[1150e3,1e3]], | ||
| "temperature models":[{"model":"linear", "max depth":95e3, "bottom temperature":1685.018071264}], | ||
| "composition models":[{"model":"uniform", "compositions":[2], "max depth":30e3}, | ||
| {"model":"uniform", "compositions":[3], "min depth":30e3, "max depth":95e3}]}, | ||
|
|
||
| {"model":"subducting plate", "name":"Subducting plate", "coordinates":[[1150e3,-1e3],[1150e3,1e3]], "dip point":[2000e3,0], | ||
| "segments":[{"length":200e3, "thickness":[300e3], "angle":[0,45]}, {"length":200e3, "thickness":[300e3], "angle":[45]}, | ||
| {"length":200e3, "thickness":[300e3], "angle":[45,0]}, {"length":100e3, "thickness":[300e3], "angle":[0]}], | ||
| "temperature models":[ | ||
| {"model":"mass conserving", | ||
| "density":3300, "thermal conductivity":4,"adiabatic heating":true, | ||
| "spreading velocity":0.0135, | ||
| "subducting velocity":0.0135, | ||
| "ridge coordinates":[[[100e3,-1e3],[100e3,1e3]]], | ||
| "coupling depth":80e3, | ||
| "forearc cooling factor":20.0, | ||
| "taper distance":100e3, | ||
| "min distance slab top":-300e3, "max distance slab top":300e3}], | ||
| "composition models":[{"model":"uniform", "compositions":[0], "max distance slab top":10e3}, | ||
| {"model":"uniform", "compositions":[1], "min distance slab top":10e3, "max distance slab top":95e3 }]} | ||
| ] | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The working directory for the cookbooks is
/home/runner/work/WorldBuilder/WorldBuilder/tests/cookbooks/, so the data directory has to be2d_cartesian_plate_ascii. Another option would be to make the working directory the actual cookbook directory in cmake.