@@ -17,7 +17,7 @@ def main(catalog_file, mc_file, pdf_file, m_file, m_select, mag_label, mc, m_max
then the program looks for a label of ' Mw ' for magnitude in the catalog
then the program looks for a label of ' Mw ' for magnitude in the catalog
mc: The magnitude of completeness (Mc) of the catalog
mc: The magnitude of completeness (Mc) of the catalog
m_max:M_max. The magnitude distribution is estimated for the range from Mc to M_max. If no value is provided,
m_max:M_max. The magnitude distribution is estimated for the range from Mc to M_max. If no value is provided,
then the program sets M_max to be 3 magnitude units above the maximum magnitude value in the catalog.
then the program sets M_max to be 1 magnitude units above the maximum magnitude value in the catalog.
m_kde_method: The kernel density estimator to use.
m_kde_method: The kernel density estimator to use.
xy_select: If True, perform an estimation of the magnitude distribution using KDE with the chosen KDE method
xy_select: If True, perform an estimation of the magnitude distribution using KDE with the chosen KDE method
grid_dim: The grid cell size (in metres) of the final ground motion product map. A smaller cell size will
grid_dim: The grid cell size (in metres) of the final ground motion product map. A smaller cell size will
@@ -45,22 +45,19 @@ def main(catalog_file, mc_file, pdf_file, m_file, m_select, mag_label, mc, m_max
"""
"""
import sys
import sys
from importlib . metadata import version
import logging
import logging
from base_logger import getDefaultLogger
from base_logger import getDefaultLogger
from timeit import default_timer as timer
from timeit import default_timer as timer
from math import ceil , floor
from math import ceil , floor , isnan
import numpy as np
import numpy as np
import scipy
import obspy
import dask
import dask
from dask . diagnostics import ProgressBar # use Dask progress bar
from dask . diagnostics import ProgressBar # use Dask progress bar
import kalepy as kale
import kalepy as kale
import utm
import utm
from skimage . transform import resize
from skimage . transform import resize
import psutil
import openquake . engine
import igfash
import igfash
from igfash . io import read_mat_cat , read_mat_m , read_mat_pdf , read_csv
from igfash . io import read_mat_cat , read_mat_m , read_mat_mc , read_mat_pdf , read_csv
from igfash . window import win_CTL , win_CNE
from igfash . window import win_CTL , win_CNE
import igfash . kde as kde
import igfash . kde as kde
from igfash . gm import compute_IMT_exceedance
from igfash . gm import compute_IMT_exceedance
@@ -70,6 +67,8 @@ def main(catalog_file, mc_file, pdf_file, m_file, m_select, mag_label, mc, m_max
import matplotlib . pyplot as plt
import matplotlib . pyplot as plt
from matplotlib . ticker import MultipleLocator
from matplotlib . ticker import MultipleLocator
from matplotlib . contour import ContourSet
from matplotlib . contour import ContourSet
import xml . etree . ElementTree as ET
import json
logger = getDefaultLogger ( ' igfash ' )
logger = getDefaultLogger ( ' igfash ' )
@@ -82,6 +81,7 @@ def main(catalog_file, mc_file, pdf_file, m_file, m_select, mag_label, mc, m_max
m_range = [ None ]
m_range = [ None ]
else :
else :
m_range = read_mat_m ( m_file )
m_range = read_mat_m ( m_file )
m_max = m_range [ - 1 ] # take m_max from the m_file
if verbose :
if verbose :
logger . setLevel ( logging . DEBUG )
logger . setLevel ( logging . DEBUG )
@@ -103,25 +103,13 @@ verbose: {verbose}")
# print key package version numbers
# print key package version numbers
logger . debug ( f " Python version { sys . version } " )
logger . debug ( f " Python version { sys . version } " )
logger . debug ( f " Numpy version { np . __version__ } " )
logger . debug ( f " Numpy version { version ( ' numpy ' ) } " )
logger . debug ( f " Scipy version { scipy . __version__ } " )
logger . debug ( f " Scipy version { version ( ' scipy ' ) } " )
logger . debug ( f " Obspy version { obspy . __version__ } " )
logger . debug ( f " Obspy version { version ( ' obspy ' ) } " )
logger . debug ( f " Openquake version { openquake. engine . __version__ } " )
logger . debug ( f " Openquake version { version ( ' openquake. engine' ) } " )
logger . debug ( f " Igfash version { igfash . __version__ } " )
logger . debug ( f " Igfash version { version ( ' igfash ' ) } " )
logger . debug ( f " Rbeast version { version ( ' rbeast ' ) } " )
# print number of cpu cores available
logger . debug ( f " Dask version { version ( ' dask ' ) } " )
ncpu = psutil . cpu_count ( logical = False )
logger . debug ( f " Number of cpu cores available: { ncpu } " )
for process in psutil . process_iter ( ) :
with process . oneshot ( ) :
# cpu = process.cpu_percent()
cpu = process . cpu_percent ( ) / ncpu
if cpu > 1 :
logger . debug ( f " { process . name ( ) } , { cpu } " )
logger . debug ( f " BASELINE CPU LOAD% { psutil . cpu_percent ( interval = None , percpu = True ) } " )
dask . config . set ( scheduler = ' processes ' )
dask . config . set ( scheduler = ' processes ' )
@@ -143,12 +131,18 @@ verbose: {verbose}")
time , mag , lat , lon , depth = read_mat_cat ( catalog_file , mag_label = mag_label , catalog_label = ' Catalog ' )
time , mag , lat , lon , depth = read_mat_cat ( catalog_file , mag_label = mag_label , catalog_label = ' Catalog ' )
# check for null magnitude values
m_null_idx = np . where ( np . isnan ( mag ) ) [ 0 ]
if len ( m_null_idx ) > 0 :
msg = f " There are null values in the magnitude column of the catalog at indices { m_null_idx } "
logger . error ( msg )
raise Exception ( msg )
if mc != None :
if mc != None :
logger . info ( " Mc value provided by user " )
logger . info ( " Mc value provided by user " )
trim_to_mc = True
trim_to_mc = True
elif mc_file != None :
elif mc_file != None :
logger . info ( " Mc estimation output file provided; selecting largest Mc from the list " )
logger . info ( " Mc estimation output file provided; selecting largest Mc from the list " )
mc = read_mc ( mc_file )
mc = read_mat_ mc ( mc_file )
trim_to_mc = True
trim_to_mc = True
else :
else :
logger . info ( " No Mc provided; using all magnitudes from the catalog " )
logger . info ( " No Mc provided; using all magnitudes from the catalog " )
@@ -164,9 +158,10 @@ verbose: {verbose}")
lat = np . delete ( lat , indices )
lat = np . delete ( lat , indices )
lon = np . delete ( lon , indices )
lon = np . delete ( lon , indices )
# if user does not provide a m_max, set m_max to 3 magnitude units above max magnitude in catalog
# if user does not provide a m_max, set m_max to 1 magnitude unit above max magnitude in catalog
if m_max == None :
if m_max == None :
m_max = mag . max ( ) + 3 .0
m_max = mag . max ( ) + 1 .0
logger . info ( f " No m_max was given. Therefore m_max is automatically set to: { m_max } " )
start = timer ( )
start = timer ( )
@@ -231,14 +226,15 @@ verbose: {verbose}")
y_min = y . min ( )
y_min = y . min ( )
x_max = x . max ( )
x_max = x . max ( )
y_max = y . max ( )
y_max = y . max ( )
z_min = depth . min ( )
z_max = depth . max ( )
grid_x_max = int ( ceil ( x_max / grid_dim ) * grid_dim )
grid_x_max = int ( ceil ( x_max / grid_dim ) * grid_dim )
grid_x_min = int ( floor ( x_min / grid_dim ) * grid_dim )
grid_x_min = int ( floor ( x_min / grid_dim ) * grid_dim )
grid_y_max = int ( ceil ( y_max / grid_dim ) * grid_dim )
grid_y_max = int ( ceil ( y_max / grid_dim ) * grid_dim )
grid_y_min = int ( floor ( y_min / grid_dim ) * grid_dim )
grid_y_min = int ( floor ( y_min / grid_dim ) * grid_dim )
grid_lat_max , grid_lon_max = utm . to_latlon ( grid_x_max , grid_y_max , utm_zone_number , utm_zone_letter )
grid_lat_min , grid_lon_min = utm . to_latlon ( grid_x_min , grid_y_min , utm_zone_number , utm_zone_letter )
# rectangular grid
# rectangular grid
nx = int ( ( grid_x_max - grid_x_min ) / grid_dim ) + 1
nx = int ( ( grid_x_max - grid_x_min ) / grid_dim ) + 1
ny = int ( ( grid_y_max - grid_y_min ) / grid_dim ) + 1
ny = int ( ( grid_y_max - grid_y_min ) / grid_dim ) + 1
@@ -325,8 +321,8 @@ verbose: {verbose}")
lambdas = [ None ]
lambdas = [ None ]
if custom_rate != None and forecast_select :
if custom_rate != None and forecast_select :
logger . info ( f " Using activity rate specified by user: { custom_rate } per { time_unit } " )
logger . info ( f " Using activity rate specified by user: { custom_rate } per { time_unit } " )
lambdas = [ custom_rate ]
lambdas = np . array ( [ custom_rate ] , dtype = ' d ' )
lambdas_perc = [ 1 ]
lambdas_perc = np . array ( [ 1 ] , dtype = ' d ' )
elif rate_select :
elif rate_select :
logger . info ( f " Activity rate modeling selected " )
logger . info ( f " Activity rate modeling selected " )
@@ -344,6 +340,12 @@ verbose: {verbose}")
elif time_unit == ' years ' :
elif time_unit == ' years ' :
multiplicator = 1 / 365
multiplicator = 1 / 365
# Raise an exception when time_win_duration from the user is too large relative to the catalog
if time_win_duration / multiplicator > 0.5 * ( time [ - 1 ] - time [ 0 ] ) :
msg = " Activity rate estimation time window must be less than half the catalog length. Use a shorter time window. "
logger . error ( msg )
raise Exception ( msg )
# Selects dates in datenum format and procceeds to forecast value
# Selects dates in datenum format and procceeds to forecast value
start_date = datenum_data [ - 1 ] - ( 2 * time_win_duration / multiplicator )
start_date = datenum_data [ - 1 ] - ( 2 * time_win_duration / multiplicator )
dates_calc = [ date for date in datenum_data if start_date < = date < = datenum_data [ - 1 ] ]
dates_calc = [ date for date in datenum_data if start_date < = date < = datenum_data [ - 1 ] ]
@@ -360,7 +362,7 @@ verbose: {verbose}")
act_rate , bin_counts , bin_edges , out , pprs , rt , idx , u_e = calc_bins ( np . array ( datenum_data ) , time_unit ,
act_rate , bin_counts , bin_edges , out , pprs , rt , idx , u_e = calc_bins ( np . array ( datenum_data ) , time_unit ,
time_win_duration , dates_calc ,
time_win_duration , dates_calc ,
rate_forecast , rate_unc_high , rate_unc_low ,
rate_forecast , rate_unc_high , rate_unc_low ,
multiplicator , quiet = True )
multiplicator , quiet = True , figsize = ( 14 , 9 ) )
# Assign probabilities
# Assign probabilities
lambdas , lambdas_perc = lambda_probs ( act_rate , dates_calc , bin_edges )
lambdas , lambdas_perc = lambda_probs ( act_rate , dates_calc , bin_edges )
@@ -372,18 +374,29 @@ verbose: {verbose}")
if forecast_select :
if forecast_select :
products = products_string . split ( )
products = products_string . split ( )
logger . info (
logger . info ( f " Ground motion forecasting selected with ground motion model { model } and IMT products { products_string } " )
f " Ground motion forecasting selected with ground motion model { model } and IMT products { products_string } " )
# validate m_max against the grond motion model
models_anthro_limited = [ ' Lasocki2013 ' , ' Atkinson2015 ' , ' ConvertitoEtAl2012Geysers ' ] # these models require that m_max<=4.5
if m_max > 4.5 and model in models_anthro_limited :
if m_file is None :
msg = f " The selected ground motion model { model } is only valid for magnitudes up to 4.5. Please select a lower maximum magnitude. "
else :
msg = f " The selected ground motion model { model } is only valid for magnitudes up to 4.5, but the provided magnitude file includes values up to { m_max } . Please adjust the magnitude range in the file accordingly. "
logger . error ( msg )
raise Exception ( msg )
if not xy_select :
if not xy_select :
msg = " Event location distribution modeling was not selected; cannot continue... "
msg = " Event location distribution modeling was not selected; cannot continue... "
logger . error ( msg )
logger . error ( msg )
raise Exception ( msg )
raise Exception ( msg )
elif m_pdf [ 0 ] == None :
if m_pdf [ 0 ] == None :
msg = " Magnitude distribution modeling was not selected and magnitude PDF file was not provided; cannot continue... "
msg = " Magnitude distribution modeling was not selected and magnitude PDF file was not provided; cannot continue... "
logger . error ( msg )
logger . error ( msg )
raise Exception ( msg )
raise Exception ( msg )
elif lambdas [ 0 ] == None :
if lambdas [ 0 ] == None :
msg = " Activity rate modeling was not selected and custom activity rate was not provided; cannot continue... "
msg = " Activity rate modeling was not selected and custom activity rate was not provided; cannot continue... "
logger . error ( msg )
logger . error ( msg )
raise Exception ( msg )
raise Exception ( msg )
@@ -421,7 +434,10 @@ verbose: {verbose}")
rx_lat [ i ] , rx_lon [ i ] = utm . to_latlon ( x_rx [ i ] , y_rx [ i ] , utm_zone_number ,
rx_lat [ i ] , rx_lon [ i ] = utm . to_latlon ( x_rx [ i ] , y_rx [ i ] , utm_zone_number ,
utm_zone_letter ) # get receiver location as lat,lon
utm_zone_letter ) # get receiver location as lat,lon
# experimental - compute ground motion only at grid points that hav e m inimum probability density of thresh_fxy
# convert distances from m to km because openquake ground motion models tak e input distances in kilometres
distances = distances / 1000.0
# compute ground motion only at grid points that have minimum probability density of thresh_fxy
if exclude_low_fxy :
if exclude_low_fxy :
indices = list ( np . where ( fxy . flatten ( ) > thresh_fxy ) [ 0 ] )
indices = list ( np . where ( fxy . flatten ( ) > thresh_fxy ) [ 0 ] )
else :
else :
@@ -434,25 +450,47 @@ verbose: {verbose}")
PGA = np . zeros ( shape = ( nx * ny ) )
PGA = np . zeros ( shape = ( nx * ny ) )
# use dask parallel computing
start = timer ( )
start = timer ( )
pbar = ProgressBar ( )
pbar . register ( )
# iter = range(0,len(distances))
iter = indices
iml_grid_raw = [ ] # raw ground motion grids
for imt in products :
logger . info ( f " Estimating { imt } " )
imls = [ dask . delayed ( compute_IMT_exceedance ) ( rx_lat [ i ] , rx_lon [ i ] , distances [ i ] . flatten ( ) , fr , p , lambdas ,
use_pp = False
forecast_len , lambdas_perc , m_range , m_pdf , m_cdf , model ,
log_level = logging . DEBUG , imt = imt , IMT_min = 0.0 , IMT_max = 2.0 ,
if use_pp : # use dask parallel computing
rx_label = i ) for i in iter ]
pbar = ProgressBar ( )
iml = dask . compu te( * imls )
pbar . regis ter ( )
iml_grid_raw . append ( list ( iml ) )
# iter = range(0,len(distances) )
iter = indices
iml_grid_raw = [ ] # raw ground motion grids
for imt in products :
logger . info ( f " Estimating { imt } " )
imls = [ dask . delayed ( compute_IMT_exceedance ) ( rx_lat [ i ] , rx_lon [ i ] , distances [ i ] . flatten ( ) , fr , p , lambdas ,
forecast_len , lambdas_perc , m_range , m_pdf , m_cdf , model ,
log_level = logging . DEBUG , imt = imt , IMT_min = 0.0 , IMT_max = 2.0 , rx_label = i ,
rtol = 0.1 , use_cython = True ) for i in iter ]
iml = dask . compute ( * imls )
iml_grid_raw . append ( list ( iml ) )
else :
iml_grid_raw = [ ]
iter = indices
for imt in products :
iml = [ ]
for i in iter :
iml_i = compute_IMT_exceedance ( rx_lat [ i ] , rx_lon [ i ] , distances [ i ] . flatten ( ) , fr , p , lambdas , forecast_len ,
lambdas_perc , m_range , m_pdf , m_cdf , model , imt = imt , IMT_min = 0.0 ,
IMT_max = 2.0 , rx_label = i , rtol = 0.1 , use_cython = True )
iml . append ( iml_i )
logger . info ( f " Estimated { imt } at rx { i } is { iml_i } " )
iml_grid_raw . append ( iml )
end = timer ( )
end = timer ( )
logger . info ( f " Ground motion exceedance computation time: { round ( end - start , 1 ) } seconds " )
logger . info ( f " Ground motion exceedance computation time: { round ( end - start , 1 ) } seconds " )
if np . isnan ( iml_grid_raw ) . all ( ) :
msg = " No valid ground motion intensity measures were forecasted. Try a different ground motion model. "
logger . error ( msg )
raise Exception ( msg )
# create list of one empty list for each imt
# create list of one empty list for each imt
iml_grid = [ [ ] for _ in range ( len ( products ) ) ] # final ground motion grids
iml_grid = [ [ ] for _ in range ( len ( products ) ) ] # final ground motion grids
iml_grid_prep = iml_grid . copy ( ) # temp ground motion grids
iml_grid_prep = iml_grid . copy ( ) # temp ground motion grids
@@ -468,16 +506,6 @@ verbose: {verbose}")
else :
else :
iml_grid_prep = iml_grid_raw
iml_grid_prep = iml_grid_raw
# TODO Remove. Saving coordinates to
# north, south = lat.max(), lat.min()
# east, west = lon.max(), lon.min()
# bbox_json = {"south": float(south), "west": float(west),
# "north": float(north), "east": float(east)}
# with open("overlay_bounds.json", "w", encoding="utf‑ 8") as fh:
# json.dump(bbox_json, fh, indent=2)
#
# logger.info(f"Saved bbox to overlay_bounds.json → {bbox_json}")
for j in range ( 0 , len ( products ) ) :
for j in range ( 0 , len ( products ) ) :
vmin = min ( x for x in iml_grid_prep [ j ] if x is not np . nan )
vmin = min ( x for x in iml_grid_prep [ j ] if x is not np . nan )
vmax = max ( x for x in iml_grid_prep [ j ] if x is not np . nan )
vmax = max ( x for x in iml_grid_prep [ j ] if x is not np . nan )
@@ -491,6 +519,12 @@ verbose: {verbose}")
mode = ' reflect ' , anti_aliasing = False )
mode = ' reflect ' , anti_aliasing = False )
iml_grid_hd [ iml_grid_hd == 0.0 ] = np . nan # change zeroes back to nan
iml_grid_hd [ iml_grid_hd == 0.0 ] = np . nan # change zeroes back to nan
# trim edges so the grid is not so blocky
vmin_hd = min ( x for x in iml_grid_hd . flatten ( ) if not isnan ( x ) )
vmax_hd = max ( x for x in iml_grid_hd . flatten ( ) if not isnan ( x ) )
trim_thresh = vmin
iml_grid_hd [ iml_grid_hd < trim_thresh ] = np . nan
# generate image overlay
# generate image overlay
north , south = lat . max ( ) , lat . min ( ) # Latitude range
north , south = lat . max ( ) , lat . min ( ) # Latitude range
east , west = lon . max ( ) , lon . min ( ) # Longitude range
east , west = lon . max ( ) , lon . min ( ) # Longitude range
@@ -499,55 +533,27 @@ verbose: {verbose}")
map_center = [ np . mean ( [ north , south ] ) , np . mean ( [ east , west ] ) ]
map_center = [ np . mean ( [ north , south ] ) , np . mean ( [ east , west ] ) ]
# Create an image from the grid
# Create an image from the grid
cmap_name = ' YlOrRd '
cmap = plt . get_cmap ( cmap_name )
fig , ax = plt . subplots ( figsize = ( 6 , 6 ) )
fig , ax = plt . subplots ( figsize = ( 6 , 6 ) )
ax . imshow ( iml_grid_hd , origin = ' lower ' , cmap = ' viridis ' )
ax . imshow ( iml_grid_hd , origin = ' lower ' , cmap = cmap , vmin = vmin , vmax = vmax )
ax . axis ( ' off ' )
ax . axis ( ' off ' )
# Save the figure
# Save the figure
fig . canvas . draw ( )
fig . canvas . draw ( )
svg_path = f " overlay_ { j } .svg "
overlay_filename = f " overlay_ { j } .svg "
plt . savefig ( svg_path , bbox_inches = " tight " , pad_inches = 0 , transparent = True )
plt . savefig ( overlay_filename , bbox_inches = " tight " , pad_inches = 0 , transparent = True )
plt . close ( fig )
plt . close ( fig )
# Embed geographic bounding box into the SVG
import xml . etree . ElementTree as ET # <-- TODO place this with other imports
map_bounds = dict ( zip ( ( " south " , " west " , " north " , " east " ) ,
import json
map ( float , ( grid_lat_min , grid_lon_min , grid_lat_max , grid_lon_max ) ) ) )
tree = ET . parse ( overlay_filename )
# -----------------------------------------
tree . getroot ( ) . set ( " data-map-bounds " , json . dumps ( map_bounds ) )
# Inject bounding box (BBOX) metadata into the SVG
tree . write ( overlay_filename , encoding = " utf-8 " , xml_declaration = True )
# -----------------------------------------
logger . info ( f " Saved geographic bounds to SVG metadata (data-map-bounds): { overlay_filename } → { map_bounds } " )
north , south = lat . max ( ) , lat . min ( )
east , west = lon . max ( ) , lon . min ( )
bbox_dict = {
" south " : float ( south ) ,
" west " : float ( west ) ,
" north " : float ( north ) ,
" east " : float ( east )
}
tree = ET . parse ( svg_path )
root = tree . getroot ( )
# Remove any existing <metadata> tags
for old_meta in root . findall ( " { http://www.w3.org/2000/svg}metadata " ) :
root . remove ( old_meta )
# Add new <metadata> element with the bounding box JSON
meta_elem = ET . SubElement ( root , " metadata " )
meta_elem . text = json . dumps ( bbox_dict )
# (Optional) Also store the bounding box as a data attribute for quick access
root . set ( " data-bbox " , json . dumps ( bbox_dict ) )
tree . write ( svg_path , encoding = " utf-8 " , xml_declaration = True )
logger . info ( f " Embedded bbox into { svg_path } → { bbox_dict } " )
# -----------------------------------------
# END Inject bounding box (BBOX) metadata into the SVG
# -----------------------------------------
# Make the color bar
# Make the color bar
cmap_name = ' viridis '
width = 50
width = 50
height = 500
height = 500
@@ -555,16 +561,20 @@ verbose: {verbose}")
gradient = np . vstack ( ( gradient , gradient ) ) . T
gradient = np . vstack ( ( gradient , gradient ) ) . T
gradient = np . tile ( gradient , ( 1 , width ) )
gradient = np . tile ( gradient , ( 1 , width ) )
colorbar_title = products [ j ]
if " PGA " in colorbar_title or " SA " in colorbar_title :
colorbar_title = colorbar_title + " (g) "
fig , ax = plt . subplots ( figsize = ( ( width + 40 ) / 100.0 , ( height + 20 ) / 100.0 ) ,
fig , ax = plt . subplots ( figsize = ( ( width + 40 ) / 100.0 , ( height + 20 ) / 100.0 ) ,
dpi = 100 ) # Increase fig size for labels
dpi = 100 ) # Increase fig size for labels
ax . imshow ( gradient , aspect = ' auto ' , cmap = plt . get_cmap ( cmap_name ) ,
ax . imshow ( gradient , aspect = ' auto ' , cmap = cmap . reversed ( ) ,
extent = [ 0 , 1 , vmin , vmax ] ) # Note: extent order is different for vertical
extent = [ 0 , 1 , vmin , vmax_hd ] ) # Note: extent order is different for vertical
ax . set_xticks ( [ ] ) # Remove x-ticks for vertical colorbar
ax . set_xticks ( [ ] ) # Remove x-ticks for vertical colorbar
num_ticks = 11 # Show more ticks
num_ticks = 11 # Show more ticks
tick_positions = np . linspace ( vmin , vmax , num_ticks )
tick_positions = np . linspace ( vmin , vmax_hd , num_ticks )
ax . set_yticks ( tick_positions )
ax . set_yticks ( tick_positions )
ax . set_yticklabels ( [ f " { tick : .2f } " for tick in tick_positions ] ) # format tick labels
ax . set_yticklabels ( [ f " { tick : .2f } " for tick in tick_positions ] ) # format tick labels
ax . set_title ( imt , pad = 15 )
ax . set_title ( colorbar_title , loc = ' right ' , pad = 15 )
fig . subplots_adjust ( left = 0.25 , right = 0.75 , bottom = 0.05 , top = 0.95 ) # Adjust Layout
fig . subplots_adjust ( left = 0.25 , right = 0.75 , bottom = 0.05 , top = 0.95 ) # Adjust Layout
fig . savefig ( " colorbar_ " + str ( j ) + " .svg " , bbox_inches = ' tight ' )
fig . savefig ( " colorbar_ " + str ( j ) + " .svg " , bbox_inches = ' tight ' )
plt . close ( fig )
plt . close ( fig )