Project: s41467-024-47659-w | Function: DM (python)

system: You are an expert Python programmer implementing algorithms from scientific papers.
Complete the masked function based on the algorithm description.
Follow Python best practices and maintain consistent code style.
Return **only** the complete Python function code.

user: Complete this Python function:

**DESCRIPTION:** RNA duplex-monomer equilibrium calculator implementing quadratic formula solution for temperature-dependent RNA hybridization thermodynamics following van't Hoff analysis

**KEY TERMS:** {
  "LT": "total RNA ligand concentration",
  "Keq": "temperature-dependent equilibrium constant",
  "Monomer": "single-stranded RNA concentration",
  "Dimer": "double-stranded RNA concentration",
  "P_Monomer": "fractional population of ssRNA",
  "P_Dimer": "fractional population of dsRNA"
}

**CONTEXT CODE:** # -*- coding: utf-8 -*-
"""
# Fitting of UA-12mer dsRNA and ssRNA signal intensities as a function of temperature
# to obtain deltaH and deltaS values.

# numpy ver. 1.24.3
# matplotlib ver. 3.7.1
# pandas ver. 1.5.3
# lmfit ver. 1.2.2
"""


import numpy as np
import matplotlib.pyplot as plt
from lmfit import minimize, Parameters, fit_report
import pandas as pd


#########################
## Functions
#########################

R = 1.99  #cal/(K*mol)

def DM(LT,Keq):
    # MASKED: RNA duplex-monomer equilibrium calculator implementing quadratic formula solution for temperature-dependent RNA hybridization thermodynamics following van't Hoff analysis
    raise NotImplementedError("This function is masked for testing purposes.")

def Keq(T,dH,dS):
    return np.exp((-dH+T*dS)/(R*T))

#########################
## Data import
#########################
# Total RNA concentration [M]

LT = 100E-6

# Input data file
def load_duplex_data():
    try:
        return pd.read_csv("input.csv")
    except FileNotFoundError:
        # For testing purposes when run from different directory
        return None

data = load_duplex_data()

# Temperature in degree
# Datapoints from 22.5 to 40 deg were used for fitting.
# Three spectra were recorded at each temperature.

temp = np.array([22.5,25,27.5,30,32.5,35,37.5,40])

ds_pop = np.zeros(len(temp))
ds_pop_e = np.zeros(len(temp))
ss_pop = np.zeros(len(temp))
ss_pop_e = np.zeros(len(temp))

if __name__ == "__main__":
    if data is None:
        print("No data file found - running in test mode")
        exit()

    for i in range(len(temp)):
        ds=np.array(data[(data['Temp']==temp[i])].Vol_ds)
        ss=np.array(data[(data['Temp']==temp[i])].Vol_ss)
        ds_pop[i] = np.average(ds/(ds+ss))
        ss_pop[i] = np.average(ss/(ds+ss))
        ds_pop_e[i] = np.std(ds/(ds+ss))
        ss_pop_e[i] = np.std(ss/(ds+ss))
   
   
    #########################
    ## Fitting
    #########################
    
    fit_params = Parameters()
    fit_params.add('dH',value=60000,vary=True)
    fit_params.add('dS',value=212,vary=True)
    
    def objective(fit_params):
        M = np.zeros(len(temp))
        D = np.zeros(len(temp))
        dH = fit_params['dH']
        dS = fit_params['dS']
        for i in range(len(temp)):
            K = Keq(temp[i]+273.15,dH,dS)
            M[i],D[i] = DM(LT,K)
        
        return (D-ds_pop)
    
    
    result = minimize(objective,fit_params)
    print(fit_report(result))
    with open("report.txt", 'w') as fh:
        fh.write(fit_report(result))
    
    opt_params = result.params
    opt_dH = opt_params["dH"].value 
    std_dH = opt_params["dH"].stderr
    opt_dS = opt_params["dS"].value 
    std_dS = opt_params["dS"].stderr
    
    #########################
    ## Plotting
    #########################
    
    # Get smoothlines for plotting
    simtemp=np.linspace(np.min(temp)-5,np.max(temp)+5,100)
    Msim = np.zeros(len(simtemp))
    Dsim = np.zeros(len(simtemp))
    for i in range(len(simtemp)):
        K = Keq(simtemp[i]+273.15,opt_dH,opt_dS)
        Msim[i],Dsim[i] = DM(LT,K)
        
    fig1 = plt.figure(figsize=(4,3))
    ax1 = fig1.add_subplot(111)
    ax1.errorbar(temp,ds_pop,yerr=ds_pop_e,fmt='o', capsize=4,markersize=5,label="dsRNA",color="skyblue")
    ax1.errorbar(temp,ss_pop,yerr=ss_pop_e,fmt='o', capsize=4, markersize=5,label="ssRNA",color="tomato")
    ax1.plot(simtemp,Dsim,color="skyblue")
    ax1.plot(simtemp,Msim,color="tomato")
    ax1.set_title("$\\Delta$H = " + str(round(opt_dH/1000,2)) + " kcal/mol, "+
              "$\\Delta$S = " + str(round(opt_dS,2)) + " cal/(K*mol)")


Complete the function implementation replacing "raise NotImplementedError".